smbtxt.c 7.33 KB
Newer Older
1 2 3 4 5 6 7 8
/* Synchronet message base (SMB) message text library routines */

/* $Id$ */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
rswindell's avatar
rswindell committed
9
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
 *																			*
 * This library is free software; you can redistribute it and/or			*
 * modify it under the terms of the GNU Lesser General Public License		*
 * as published by the Free Software Foundation; either version 2			*
 * of the License, or (at your option) any later version.					*
 * See the GNU Lesser General Public License for more details: lgpl.txt or	*
 * http://www.fsf.org/copyleft/lesser.html									*
 *																			*
 * Anonymous FTP access to the most recent released source is available at	*
 * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
 *																			*
 * Anonymous CVS access to the development source and modification history	*
 * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
 *     (just hit return, no password is necessary)							*
 * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * You are encouraged to submit any modifications (preferably in Unix diff	*
 * format) via e-mail to mods@synchro.net									*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

/* ANSI */
37 38
#include <stdlib.h>	/* malloc/realloc/free */
#include <string.h>	/* strlen */
39 40 41 42

/* SMB-specific */
#include "smblib.h"

43
char* SMBCALL smb_getmsgtxt(smb_t* smb, smbmsg_t* msg, ulong mode)
44
{
45 46 47
	char*	buf;
	char*	lzhbuf;
	char*	p;
48
	char*	str;
deuce's avatar
64-bit  
deuce committed
49
	uint16_t	xlat;
50 51
	uint 	i;
	int		lzh;	/* BOOL */
52 53
	long	l=0,lzhlen,length;

54
	if((buf=(char*)malloc(sizeof(char)))==NULL) {
55
		sprintf(smb->last_error
56 57
			,"%s malloc failure of %" XP_PRIsize_t "u bytes for buffer"
			,__FUNCTION__, sizeof(char));
58 59 60 61
		return(NULL);
	}
	*buf=0;

62 63 64 65 66 67 68 69
	if(!(mode&GETMSGTXT_NO_HFIELDS)) {
		for(i=0;i<(uint)msg->total_hfields;i++) {			/* comment headers are part of text */
			if(msg->hfield[i].type!=SMB_COMMENT && msg->hfield[i].type!=SMTPSYSMSG)
				continue;
			str=(char*)msg->hfield_dat[i];
			length=strlen(str)+2;	/* +2 for crlf */
			if((p=(char*)realloc(buf,l+length+1))==NULL) {
				sprintf(smb->last_error
70 71
					,"%s realloc failure of %ld bytes for comment buffer"
					, __FUNCTION__, l+length+1);
rswindell's avatar
rswindell committed
72 73
				free(buf);
				return(NULL);
74 75 76
			}
			buf=p;
			l+=sprintf(buf+l,"%s\r\n",str);
77
		}
rswindell's avatar
rswindell committed
78 79 80
		if(l) {	/* Add a blank line after comments */
			if((p=(char*)realloc(buf,l+3))==NULL) {
				sprintf(smb->last_error
81 82
					,"%s realloc failure of %ld bytes for comment buffer"
					, __FUNCTION__, l+3);
rswindell's avatar
rswindell committed
83 84 85 86 87 88 89 90 91 92 93 94 95 96
				free(buf);
				return(NULL);
			}
			buf=p;
			l+=sprintf(buf+l,"\r\n");
		}
		unsigned answers = 0;
		for(i=0;i<(uint)msg->total_hfields;i++) {			/* Poll Answers are part of text */
			if(msg->hfield[i].type!=SMB_POLL_ANSWER)
				continue;
			char tmp[128];
			length = safe_snprintf(tmp, sizeof(tmp), "%2u: %s\r\n", ++answers, (char*)msg->hfield_dat[i]);
			if((p=(char*)realloc(buf,l+length+1))==NULL) {
				sprintf(smb->last_error
97 98
					,"%s realloc failure of %ld bytes for comment buffer"
					, __FUNCTION__, l+length+1);
rswindell's avatar
rswindell committed
99 100 101 102 103 104 105 106
				free(buf);
				return(NULL);
			}
			buf=p;
			memcpy(buf+l, tmp, length);
			l += length;
			buf[l] = 0;
		}
107 108
	}

109
	for(i=0;i<(uint)msg->hdr.total_dfields;i++) {
110
		if(msg->dfield[i].length<=sizeof(xlat))
111
			continue;
112 113 114 115 116 117 118 119 120 121 122 123
		switch(msg->dfield[i].type) {
			case TEXT_BODY:
				if(mode&GETMSGTXT_NO_BODY)
					continue;
				break;
			case TEXT_TAIL:
				if(!(mode&GETMSGTXT_TAILS))
					continue;
				break;
			default:	/* ignore other data types */
				continue;
		}
124 125
		fseek(smb->sdt_fp,msg->hdr.offset+msg->dfield[i].offset
			,SEEK_SET);
126
		fread(&xlat,sizeof(xlat),1,smb->sdt_fp);
127 128 129
		lzh=0;
		if(xlat==XLAT_LZH) {
			lzh=1;
130
			fread(&xlat,sizeof(xlat),1,smb->sdt_fp); 
131 132 133 134
		}
		if(xlat!=XLAT_NONE) 	/* no other translations currently supported */
			continue;

135
		length=msg->dfield[i].length-sizeof(xlat);
136
		if(lzh) {
137
			length-=sizeof(xlat);
138 139
			if(length<1)
				continue;
140
			if((lzhbuf=(char*)malloc(length))==NULL) {
141
				sprintf(smb->last_error
142 143
					,"%s malloc failure of %ld bytes for LZH buffer"
					, __FUNCTION__, length);
rswindell's avatar
rswindell committed
144 145
				free(buf);
				return(NULL);
146
			}
147
			smb_fread(smb,lzhbuf,length,smb->sdt_fp);
deuce's avatar
deuce committed
148
			lzhlen=*(int32_t*)lzhbuf;
149
			if((p=(char*)realloc(buf,l+lzhlen+3L))==NULL) {
150
				sprintf(smb->last_error
151 152
					,"%s realloc failure of %ld bytes for text buffer"
					, __FUNCTION__, l+lzhlen+3L);
153
				free(lzhbuf);
rswindell's avatar
rswindell committed
154 155
				free(buf);
				return(NULL); 
156 157
			}
			buf=p;
deuce's avatar
deuce committed
158
			lzh_decode((uint8_t *)lzhbuf,length,(uint8_t *)buf+l);
159
			free(lzhbuf);
160 161 162
			l+=lzhlen; 
		}
		else {
163
			if((p=(char*)realloc(buf,l+length+3L))==NULL) {
164
				sprintf(smb->last_error
165 166
					,"%s realloc failure of %ld bytes for text buffer"
					, __FUNCTION__, l+length+3L);
rswindell's avatar
rswindell committed
167 168
				free(buf);
				return(NULL);
169 170 171 172 173 174 175 176 177 178
			}
			buf=p;
			p=buf+l;
			l+=fread(p,1,length,smb->sdt_fp);
		}
		if(!l)
			continue;
		l--;
		while(l && buf[l]==0) l--;
		l++;
179
		*(buf+l)='\r';	/* CR */
180
		l++;
181
		*(buf+l)='\n';	/* LF */
182 183 184
		l++;
		*(buf+l)=0; 
	}
185

rswindell's avatar
rswindell committed
186 187
	if(mode&GETMSGTXT_PLAIN)
		buf = smb_getplaintext(msg, buf);
188 189 190
	return(buf);
}

191
void SMBCALL smb_freemsgtxt(char* buf)
192 193
{
	if(buf!=NULL)
194
		free(buf);
195
}
rswindell's avatar
rswindell committed
196

197 198
/* Get just the plain-text portion of a MIME-encoded message body, recursively */
static char* mime_getplaintext(char* buf, const char* content_type, int depth)
rswindell's avatar
rswindell committed
199 200 201 202 203
{
	char*	txt;
	char*	p;
	char	boundary[256];

204 205
	if(depth > 2)
		return NULL;
rswindell's avatar
rswindell committed
206
	if(content_type == NULL)	/* Not MIME-encoded */
207
		return NULL;
rswindell's avatar
rswindell committed
208 209 210 211 212 213 214
	content_type += 13;
	SKIP_WHITESPACE(content_type);
	if(strstr(content_type, "multipart/alternative;") == content_type)
		content_type += 22;
	else if(strstr(content_type, "multipart/mixed;") == content_type)
		content_type +=16;
	else
215
		return NULL;
216
	p = strstr(content_type, "boundary=");
rswindell's avatar
rswindell committed
217
	if(p == NULL)
218
		return NULL;
219 220 221 222 223 224
	p += 9;
	if(*p == '"')
		p++;
	SAFEPRINTF(boundary, "--%s", p);
	if((p = strchr(boundary,'"')) != NULL)
		*p = 0;
rswindell's avatar
rswindell committed
225 226 227 228
	txt = buf;
	while((p = strstr(txt, boundary)) != NULL) {
		txt = p+strlen(boundary);
		SKIP_WHITESPACE(txt);
229
		if(strncmp(txt, "Content-Type:", 13) != 0)
rswindell's avatar
rswindell committed
230 231 232 233
			continue;
		p = strstr(txt, "\r\n\r\n");	/* End of header */
		if(p==NULL)
			continue;
234 235 236 237 238 239
		if(strncmp(txt, "Content-Type: text/plain;", 25) 
			&& strncmp(txt,"Content-Type: text/plain\r", 25)) {
			if((p = mime_getplaintext(p, txt, depth + 1)) != NULL)
				return p;
			continue;
		}
rswindell's avatar
rswindell committed
240 241 242 243
		txt = p;
		SKIP_WHITESPACE(txt);
		if((p = strstr(txt, boundary)) != NULL)
			*p = 0;
244
		return txt;
rswindell's avatar
rswindell committed
245
	}
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
	return NULL;
}


/* Get just the plain-text portion of a MIME-encoded message body */
char* SMBCALL smb_getplaintext(smbmsg_t* msg, char* buf)
{
	int		i;
	char*	txt;
	char*	content_type = NULL;

	for(i=0;i<msg->total_hfields;i++) { 
		if(msg->hfield[i].type==RFC822HEADER) { 
			if(strnicmp((char*)msg->hfield_dat[i],"Content-Type:",13)==0) {
				content_type=msg->hfield_dat[i];
				break;
			}
        }
    }
	txt = mime_getplaintext(buf, content_type, 0);
	if(txt != NULL)
		memmove(buf, txt, strlen(txt)+1);
rswindell's avatar
rswindell committed
268 269
	return buf;
}