Synchronet now requires the libarchive development package (e.g. libarchive-dev on Debian-based Linux distros, libarchive.org for more info) to build successfully.

smblib.c 69.5 KB
Newer Older
1 2 3 4 5 6
/* Synchronet message base (SMB) library routines */

/****************************************************************************
 * @format.tab-size 4		(Plain Text/Source Code File Header)			*
 * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
 *																			*
7
 * Copyright Rob Swindell - http://www.synchro.net/copyright.html			*
8 9 10 11 12 13 14 15 16 17 18 19 20 21
 *																			*
 * 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									*
 *																			*
 * For Synchronet coding style and modification guidelines, see				*
 * http://www.synchro.net/source.html										*
 *																			*
 * Note: If this box doesn't appear square, then you need to fix your tabs.	*
 ****************************************************************************/

22
/* ANSI C Library headers */
23

24 25 26 27
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
28
#include <stdlib.h>		/* malloc */
29 30
#include <string.h>
#include <sys/types.h>
31
#include <sys/stat.h>	/* must come after sys/types.h */
32 33

/* SMB-specific headers */
34
#include "smblib.h"
rswindell's avatar
rswindell committed
35 36
#include "genwrap.h"
#include "filewrap.h"
37 38

/* Use smb_ver() and smb_lib_ver() to obtain these values */
39 40
#define SMBLIB_VERSION		"3.00"      /* SMB library version */
#define SMB_VERSION 		0x0300		/* SMB format version */
41 42
										/* High byte major, low byte minor */

43 44
static char* nulstr="";

45
int smb_ver(void)
46 47 48 49
{
	return(SMB_VERSION);
}

50
char* smb_lib_ver(void)
51 52 53 54 55 56 57 58
{
	return(SMBLIB_VERSION);
}

/****************************************************************************/
/* Open a message base of name 'smb->file'                                  */
/* Opens files for READing messages or updating message indices only        */
/****************************************************************************/
59
int smb_open(smb_t* smb)
60
{
61
	int			i;
62 63
	time_t		start=0;
	smbhdr_t	hdr;
64

65
	/* Set default values, if uninitialized */
66
	if(!smb->retry_time)
67 68 69 70
		smb->retry_time=10;		/* seconds */
	if(!smb->retry_delay 
		|| smb->retry_delay>(smb->retry_time*100))	/* at least ten retries */
		smb->retry_delay=250;	/* milliseconds */
71
	smb->shd_fp=smb->sdt_fp=smb->sid_fp=NULL;
72
	smb->sha_fp=smb->sda_fp=smb->hash_fp=NULL;
73
	smb->last_error[0]=0;
rswindell's avatar
rswindell committed
74
	smb->locked = FALSE;
75 76 77 78 79 80 81 82 83 84 85

	/* Check for message-base lock semaphore file (under maintenance?) */
	while(smb_islocked(smb)) {
		if(!start)
			start=time(NULL);
		else
			if(time(NULL)-start>=(time_t)smb->retry_time)
				return(SMB_ERR_TIMEOUT); 
		SLEEP(smb->retry_delay);
	}

86 87
	if((i=smb_open_fp(smb,&smb->shd_fp,SH_DENYNO))!=SMB_SUCCESS)
		return(i);
88

89
	memset(&(smb->status),0,sizeof(smb->status));
90
	if(filelength(fileno(smb->shd_fp))>=(long)sizeof(smbhdr_t)) {
91
		if(smb_locksmbhdr(smb)!=SMB_SUCCESS) {
92
			smb_close(smb);
93
			/* smb_locksmbhdr set last_error */
94
			return(SMB_ERR_LOCK); 
95 96
		}
		memset(&hdr,0,sizeof(smbhdr_t));
97
		if(smb_fread(smb,&hdr,sizeof(smbhdr_t),smb->shd_fp)!=sizeof(smbhdr_t)) {
98
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
99
				,"%s reading header", __FUNCTION__);
100
			smb_close(smb);
101
			return(SMB_ERR_READ);
102
		}
103
		if(memcmp(hdr.smbhdr_id,SMB_HEADER_ID,LEN_HEADER_ID) && !smb->continue_on_error) {
104
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
105
				,"%s corrupt SMB header ID: %02X %02X %02X %02X", __FUNCTION__
106 107 108 109
				,hdr.smbhdr_id[0]
				,hdr.smbhdr_id[1]
				,hdr.smbhdr_id[2]
				,hdr.smbhdr_id[3]
110
				);
111
			smb_close(smb);
112
			return(SMB_ERR_HDR_ID); 
113
		}
114
		if(hdr.version<0x110 && !smb->continue_on_error) {         /* Compatibility check */
115
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
116
				,"%s insufficient header version: %X", __FUNCTION__
117
				,hdr.version);
118
			smb_close(smb);
119
			return(SMB_ERR_HDR_VER); 
120
		}
121
		if(smb_fread(smb,&(smb->status),sizeof(smbstatus_t),smb->shd_fp)!=sizeof(smbstatus_t)) {
122
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
123
				,"%s reading status", __FUNCTION__);
124
			smb_close(smb);
125
			return(SMB_ERR_READ); 
126
		}
127 128 129 130
		if((i=smb_unlocksmbhdr(smb))!=SMB_SUCCESS) {
			smb_close(smb);
			return(i);
		}
131 132
	}

133 134
	if((i=smb_open_fp(smb,&smb->sdt_fp,SH_DENYNO))!=SMB_SUCCESS)
		return(i);
135

136 137
	if((i=smb_open_index(smb)) != SMB_SUCCESS)
		return i;
138

139
	return(SMB_SUCCESS);
140 141
}

142
int smb_open_index(smb_t* smb)
143 144 145 146
{
	return smb_open_fp(smb, &smb->sid_fp, SH_DENYNO);
}

147 148 149
/****************************************************************************/
/* Closes the currently open message base									*/
/****************************************************************************/
150
void smb_close(smb_t* smb)
151 152 153
{
	if(smb->shd_fp!=NULL) {
		smb_unlocksmbhdr(smb);		   /* In case it's been locked */
154
		smb_close_fp(&smb->shd_fp); 
155
	}
156 157 158 159 160
	smb_close_fp(&smb->sdt_fp);
	smb_close_fp(&smb->sid_fp);
	smb_close_fp(&smb->sda_fp);
	smb_close_fp(&smb->sha_fp);
	smb_close_fp(&smb->hash_fp);
161 162
}

163 164 165 166 167 168
/****************************************************************************/
/* This set of functions is used to exclusively-lock an entire message base	*/
/* against any other process opening any of the message base files.			*/
/* Currently, this is only used while smbutil packs a message base.			*/
/* This is achieved with a semaphore lock file (e.g. mail.lock).			*/
/****************************************************************************/
169
static char* smb_lockfname(smb_t* smb, char* fname, size_t maxlen)
170
{
171
	safe_snprintf(fname,maxlen,"%s.lock",smb->file);
172 173 174
	return(fname);
}

175 176 177 178
/****************************************************************************/
/* This function is used to lock an entire message base for exclusive		*/
/* (typically for maintenance/repair)										*/
/****************************************************************************/
179
int smb_lock(smb_t* smb)
180
{
181
	char	path[MAX_PATH+1];
182
	int		file;
183
	time_t	start=0;
184

185 186 187 188 189 190 191
	smb_lockfname(smb,path,sizeof(path)-1);
	while((file=open(path,O_CREAT|O_EXCL|O_RDWR,S_IREAD|S_IWRITE))==-1) {
		if(!start)
			start=time(NULL);
		else
			if(time(NULL)-start>=(time_t)smb->retry_time) {
				safe_snprintf(smb->last_error,sizeof(smb->last_error)
192
					,"%s %d '%s' creating %s", __FUNCTION__
193
					,get_errno(),STRERROR(get_errno()),path);
194 195 196
				return(SMB_ERR_LOCK);
			}
		SLEEP(smb->retry_delay);
197 198
	}
	close(file);
199
	return(SMB_SUCCESS);
200 201
}

202
int smb_unlock(smb_t* smb)
203
{
204
	char	path[MAX_PATH+1];
205

206 207
	smb_lockfname(smb,path,sizeof(path)-1);
	if(remove(path)!=0) {
208
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
209
			,"%s %d '%s' removing %s", __FUNCTION__
210
			,get_errno(),STRERROR(get_errno()),path);
211 212
		return(SMB_ERR_DELETE);
	}
213
	return(SMB_SUCCESS);
214 215
}

216
BOOL smb_islocked(smb_t* smb)
217
{
218
	char	path[MAX_PATH+1];
219

220
	if(access(smb_lockfname(smb,path,sizeof(path)-1),0)!=0)
221
		return(FALSE);
222
	safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s %s exists", __FUNCTION__,path);
223
	return(TRUE);
224 225
}

226 227
/****************************************************************************/
/* Truncates header file													*/
228
/* Retries for smb.retry_time number of seconds								*/
229 230
/* Return 0 on success, non-zero otherwise									*/
/****************************************************************************/
231
int smb_trunchdr(smb_t* smb)
232
{
233
	time_t	start=0;
234

235
	if(smb->shd_fp==NULL) {
236
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s msgbase not open", __FUNCTION__);
237 238
		return(SMB_ERR_NOT_OPEN);
	}
239 240
	rewind(smb->shd_fp);
	while(1) {
241
		if(chsize(fileno(smb->shd_fp),0L)==0)
242
			break;
243
		if(get_errno()!=EACCES && get_errno()!=EAGAIN) {
244
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
245
				,"%s %d '%s' changing header file size", __FUNCTION__
246
				,get_errno(),STRERROR(get_errno()));
247
			return(SMB_ERR_WRITE);
248 249 250 251
		}
		if(!start)
			start=time(NULL);
		else
252
			if(time(NULL)-start>=(time_t)smb->retry_time) {	 /* Time-out */
253
				safe_snprintf(smb->last_error,sizeof(smb->last_error)
254 255
					,"%s timeout changing header file size (retry_time=%lu)", __FUNCTION__
					,(ulong)smb->retry_time);
256
				return(SMB_ERR_TIMEOUT); 
257
			}
rswindell's avatar
rswindell committed
258
		SLEEP(smb->retry_delay);
259
	}
260
	return(SMB_SUCCESS);
261 262 263 264 265 266 267 268 269
}

/*********************************/
/* Message Base Header Functions */
/*********************************/

/****************************************************************************/
/* Attempts for smb.retry_time number of seconds to lock the msg base hdr	*/
/****************************************************************************/
270
int smb_locksmbhdr(smb_t* smb)
271
{
272
	time_t	start=0;
273

274 275 276
	if(smb->locked)
		return SMB_SUCCESS;

277
	if(smb->shd_fp==NULL) {
278
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s msgbase not open", __FUNCTION__);
279 280
		return(SMB_ERR_NOT_OPEN);
	}
281
	while(1) {
282
		if(lock(fileno(smb->shd_fp),0L,sizeof(smbhdr_t)+sizeof(smbstatus_t))==0) {
283
			smb->locked=TRUE;
284
			return(SMB_SUCCESS);
285
		}
286 287 288
		if(!start)
			start=time(NULL);
		else
289 290
			if(time(NULL)-start>=(time_t)smb->retry_time)
				break;
291
		/* In case we've already locked it */
292
		if(unlock(fileno(smb->shd_fp),0L,sizeof(smbhdr_t)+sizeof(smbstatus_t))==0)
293
			smb->locked=FALSE;
294 295 296
		else {
			SLEEP(smb->retry_delay);
		}
297
	}
298
	safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s timeout locking message base", __FUNCTION__);
299
	return(SMB_ERR_TIMEOUT);
300 301 302 303 304
}

/****************************************************************************/
/* Read the SMB header from the header file and place into smb.status		*/
/****************************************************************************/
305
int smb_getstatus(smb_t* smb)
306 307 308
{
	int 	i;

309
	if(smb->shd_fp==NULL) {
310
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s msgbase not open", __FUNCTION__);
311 312
		return(SMB_ERR_NOT_OPEN);
	}
313
	fflush(smb->shd_fp);
314
	clearerr(smb->shd_fp);
315
	if(fseek(smb->shd_fp,sizeof(smbhdr_t),SEEK_SET)) {
316
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
317 318
			,"%s %d '%s' seeking to %d in header file", __FUNCTION__
			,get_errno(),STRERROR(get_errno()), (int)sizeof(smbhdr_t));
319 320
		return(SMB_ERR_SEEK);
	}
321
	i=smb_fread(smb,&(smb->status),sizeof(smbstatus_t),smb->shd_fp);
322
	if(i==sizeof(smbstatus_t))
323
		return(SMB_SUCCESS);
324
	safe_snprintf(smb->last_error,sizeof(smb->last_error)
325
		,"%s reading status", __FUNCTION__);
326
	return(SMB_ERR_READ);
327 328 329 330 331
}

/****************************************************************************/
/* Writes message base header												*/
/****************************************************************************/
332
int smb_putstatus(smb_t* smb)
333 334 335
{
	int i;

336
	if(smb->shd_fp==NULL) {
337
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s msgbase not open", __FUNCTION__);
338 339
		return(SMB_ERR_NOT_OPEN);
	}
340
	clearerr(smb->shd_fp);
341
	if(fseek(smb->shd_fp,sizeof(smbhdr_t),SEEK_SET)) {
342
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
343 344
			,"%s %d '%s' seeking to %d in header file", __FUNCTION__
			,get_errno(),STRERROR(get_errno()), (int)sizeof(smbhdr_t));
345 346
		return(SMB_ERR_SEEK);
	}
347 348 349
	i=fwrite(&(smb->status),1,sizeof(smbstatus_t),smb->shd_fp);
	fflush(smb->shd_fp);
	if(i==sizeof(smbstatus_t))
350
		return(SMB_SUCCESS);
351
	safe_snprintf(smb->last_error,sizeof(smb->last_error)
352
		,"%s writing status", __FUNCTION__);
353
	return(SMB_ERR_WRITE);
354 355 356
}

/****************************************************************************/
357
/* Unlocks previously locked message base header 							*/
358
/****************************************************************************/
359
int smb_unlocksmbhdr(smb_t* smb)
360
{
361 362
	if(smb->locked) {
		if(smb->shd_fp==NULL) {
363
			safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s msgbase not open", __FUNCTION__);
364 365 366 367
			return(SMB_ERR_NOT_OPEN);
		}
		if(unlock(fileno(smb->shd_fp),0L,sizeof(smbhdr_t)+sizeof(smbstatus_t))!=0) {
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
368
				,"%s %d '%s' unlocking message base header", __FUNCTION__,get_errno(),STRERROR(get_errno()));
369 370 371
			return(SMB_ERR_UNLOCK);
		}
		smb->locked=FALSE;
372 373
	}
	return(SMB_SUCCESS);
374 375 376 377 378 379
}

/********************************/
/* Individual Message Functions */
/********************************/

380 381 382
/****************************************************************************/
/* Is the offset a valid message header offset?								*/
/****************************************************************************/
383
BOOL smb_valid_hdr_offset(smb_t* smb, ulong offset)
384 385 386 387
{
	if(offset<sizeof(smbhdr_t)+sizeof(smbstatus_t) 
		|| offset<smb->status.header_offset) {
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
388
			,"%s invalid header offset: %lu (0x%lX)", __FUNCTION__
389 390 391 392 393 394
			,offset,offset);
		return(FALSE);
	}
	return(TRUE);
}

395 396 397
/****************************************************************************/
/* Attempts for smb.retry_time number of seconds to lock the hdr for 'msg'  */
/****************************************************************************/
398
int smb_lockmsghdr(smb_t* smb, smbmsg_t* msg)
399
{
400
	time_t	start=0;
401

402
	if(smb->shd_fp==NULL) {
403
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s msgbase not open", __FUNCTION__);
404 405
		return(SMB_ERR_NOT_OPEN);
	}
406 407 408
	if(!smb_valid_hdr_offset(smb,msg->idx.offset))
		return(SMB_ERR_HDR_OFFSET);

409
	while(1) {
410
		if(lock(fileno(smb->shd_fp),msg->idx.offset,sizeof(msghdr_t))==0)
411
			return(SMB_SUCCESS);
412 413 414
		if(!start)
			start=time(NULL);
		else
415
			if(time(NULL)-start>=(time_t)smb->retry_time) 
416 417
				break;
		/* In case we've already locked it */
418
		if(unlock(fileno(smb->shd_fp),msg->idx.offset,sizeof(msghdr_t))!=0) {
419
			SLEEP(smb->retry_delay);
420
		}
421
	}
422
	safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s timeout locking header", __FUNCTION__);
423
	return(SMB_ERR_TIMEOUT);
424 425 426 427 428 429 430 431 432
}

/****************************************************************************/
/* Fills msg->idx with message index based on msg->hdr.number				*/
/* OR if msg->hdr.number is 0, based on msg->offset (record offset).		*/
/* if msg.hdr.number does not equal 0, then msg->offset is filled too.		*/
/* Either msg->hdr.number or msg->offset must be initialized before 		*/
/* calling this function													*/
/****************************************************************************/
433
int smb_getmsgidx(smb_t* smb, smbmsg_t* msg)
434
{
435
	idxrec_t	idx;
436 437
	long		byte_offset;
	ulong		l,total,bot,top;
438 439
	off_t		length;
	size_t		idxreclen = smb_idxreclen(smb);
440

441
	if(smb->sid_fp==NULL) {
442
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s index not open", __FUNCTION__);
443
		return(SMB_ERR_NOT_OPEN);
444
	}
445 446 447
	clearerr(smb->sid_fp);

	length=filelength(fileno(smb->sid_fp));
448
	if(length<(long)idxreclen) {
449
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
450
			,"%s invalid index file length: %ld", __FUNCTION__,length);
451
		return(SMB_ERR_FILE_LEN);
452
	}
453
	total=(ulong)(length/idxreclen);
454
	if(!total) {
455
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
456
			,"%s invalid index file length: %ld", __FUNCTION__,length);
457
		return(SMB_ERR_FILE_LEN);
458 459
	}

460
	if(!msg->hdr.number) {
461 462
		if(msg->idx_offset<0)
			byte_offset=(long)(length-((-msg->idx_offset)*idxreclen));
463
		else
464
			byte_offset=msg->idx_offset*idxreclen;
465
		if(byte_offset>=length) {
466
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
467
				,"%s invalid index offset: %ld, byte offset: %ld, length: %ld", __FUNCTION__
468
				,(long)msg->idx_offset, byte_offset, length);
469 470
			return(SMB_ERR_HDR_OFFSET);
		}
471 472 473 474 475
		if(ftell(smb->sid_fp) != byte_offset) {
			if(fseek(smb->sid_fp,byte_offset,SEEK_SET)) {
				safe_snprintf(smb->last_error,sizeof(smb->last_error)
					,"%s %d '%s' seeking to offset %ld (byte %lu) in index file", __FUNCTION__
					,get_errno(),STRERROR(get_errno())
476
					,(long)msg->idx_offset,byte_offset);
477 478
				return(SMB_ERR_SEEK);
			}
479
		}
480
		if(smb_fread(smb,&msg->idx,sizeof(msg->idx),smb->sid_fp)!=sizeof(msg->idx)) {
481
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
482
				,"%s reading index at offset %ld (byte %lu)", __FUNCTION__
483
				,(long)msg->idx_offset,byte_offset);
484 485
			return(SMB_ERR_READ);
		}
486
		/* Save the correct offset (from the beginning of the file) */
487
		msg->idx_offset=byte_offset/idxreclen;
488 489 490
		return(SMB_SUCCESS); 
	}

491 492 493 494
	bot=0;
	top=total;
	l=total/2; /* Start at middle index */
	while(1) {
495
		if(l>=total) {
496
			safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s msg %lu not found"
497
				, __FUNCTION__, (ulong)msg->hdr.number);
498 499
			return(SMB_ERR_NOT_FOUND);
		}
500
		if(fseek(smb->sid_fp,l*idxreclen,SEEK_SET)) {
501
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
502
				,"%s %d '%s' seeking to offset %lu (byte %lu) in index file", __FUNCTION__
503
				,get_errno(),STRERROR(get_errno())
504
				,l,l*idxreclen);
505 506
			return(SMB_ERR_SEEK);
		}
507
		if(smb_fread(smb,&idx,sizeof(idx),smb->sid_fp)!=sizeof(idx)) {
508
			safe_snprintf(smb->last_error,sizeof(smb->last_error)
509
				,"%s reading index at offset %lu (byte %lu)", __FUNCTION__
510
				,l,l*sizeof(idx));
511
			return(SMB_ERR_READ);
512 513
		}
		if(bot==top-1 && idx.number!=msg->hdr.number) {
514
			safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s msg %lu not found"
515
				, __FUNCTION__, (ulong)msg->hdr.number);
516
			return(SMB_ERR_NOT_FOUND);
517 518 519 520 521 522 523 524 525 526 527 528 529 530
		}
		if(idx.number>msg->hdr.number) {
			top=l;
			l=bot+((top-bot)/2);
			continue; 
		}
		if(idx.number<msg->hdr.number) {
			bot=l;
			l=top-((top-bot)/2);
			continue; 
		}
		break; 
	}
	msg->idx=idx;
531
	msg->idx_offset=l;
532
	return(SMB_SUCCESS);
533 534
}

535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563
/****************************************************************************/
/* Count the number of msg index records with specific attribute flags		*/
/****************************************************************************/
uint32_t smb_count_idx_records(smb_t* smb, uint16_t mask, uint16_t cmp)
{
	int32_t offset = 0;
	uint32_t count = 0;
	for(offset = 0; ;offset++) {
		smbmsg_t msg;
		memset(&msg, 0, sizeof(msg));
		msg.idx_offset = offset;
		if(smb_getmsgidx(smb, &msg) != SMB_SUCCESS)
			break;
		if((msg.idx.attr & mask) == cmp)
			count++;
	}
	return count;
}

/****************************************************************************/
/* Returns the length (in bytes) of the SMB's index records					*/
/****************************************************************************/
size_t smb_idxreclen(smb_t* smb)
{
	if(smb->status.attr&SMB_FILE_DIRECTORY)
		return sizeof(fileidxrec_t);
	return sizeof(idxrec_t);
}

564 565 566
/****************************************************************************/
/* Reads the first index record in the open message base 					*/
/****************************************************************************/
567
int smb_getfirstidx(smb_t* smb, idxrec_t *idx)
568
{
569
	if(smb->sid_fp==NULL) {
570
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s index not open", __FUNCTION__);
571 572
		return(SMB_ERR_NOT_OPEN);
	}
573
	clearerr(smb->sid_fp);
574
	if(fseek(smb->sid_fp,0,SEEK_SET)) {
575
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
576
			,"%s %d '%s' seeking to beginning of index file", __FUNCTION__
577
			,get_errno(),STRERROR(get_errno()));
578 579
		return(SMB_ERR_SEEK);
	}
580
	if(smb_fread(smb,idx,sizeof(*idx),smb->sid_fp)!=sizeof(*idx)) {
581
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
582
			,"%s reading first index", __FUNCTION__);
583
		return(SMB_ERR_READ);
584
	}
585
	return(SMB_SUCCESS);
586 587
}

588 589 590
/****************************************************************************/
/* Reads the last index record in the open message base 					*/
/****************************************************************************/
591
int smb_getlastidx(smb_t* smb, idxrec_t *idx)
592
{
593 594
	off_t length;
	size_t idxreclen = smb_idxreclen(smb);
595

596
	if(smb->sid_fp==NULL) {
597
		safe_snprintf(smb->last_error,sizeof(smb->last_error),"%s index not open", __FUNCTION__);
598 599
		return(SMB_ERR_NOT_OPEN);
	}
600 601
	clearerr(smb->sid_fp);
	length=filelength(fileno(smb->sid_fp));
602
	if(length<(long)idxreclen) {
603
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
604
			,"%s invalid index file length: %ld", __FUNCTION__,length);
605 606
		return(SMB_ERR_FILE_LEN);
	}
607
	if(fseeko(smb->sid_fp,length-idxreclen,SEEK_SET)) {
608
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
609
			,"%s %d '%s' seeking to %u in index file", __FUNCTION__
610
			,get_errno(),STRERROR(get_errno())
611 612
			,(unsigned)(length-sizeof(idxrec_t)));
		return(SMB_ERR_SEEK);
613
	}
614
	if(smb_fread(smb,idx,sizeof(*idx),smb->sid_fp)!=sizeof(*idx)) {
615
		safe_snprintf(smb->last_error,sizeof(smb->last_error)
616
			,"%s reading last index", __FUNCTION__);
617
		return(SMB_ERR_READ);
618
	}
619
	return(SMB_SUCCESS);
620 621
}

622 623
/****************************************************************************/
/* Finds index of last message imported at or after specified time			*/
624 625
/* If you want the message base locked during this operation, the caller	*/
/* must call smb_locksmbhdr() before, smb_unlocksmbhdr() after.				*/
626
/* Returns >= 0 on success, negative (SMB_* error) on failure.				*/
627
/****************************************************************************/
628
long smb_getmsgidx_by_time(smb_t* smb, idxrec_t* match, time_t t)
629
{
630 631 632 633
    int			result;
	long		match_offset;
	ulong		total, bot, top;
	idxrec_t	idx;
634
	size_t		idxreclen = smb_idxreclen(smb);
635

636 637
	if(match == NULL)
		return SMB_BAD_PARAMETER;
638

639
	memset(match, 0, sizeof(*match));
640 641

	if(t <= 0)
642
		return SMB_BAD_PARAMETER;
643

644
	total = (ulong)(filelength(fileno(smb->sid_fp))/idxreclen);
645 646

	if(!total)	/* Empty base */
647
		return SMB_ERR_NOT_FOUND;
648

649 650 651 652 653
	if((result=smb_getlastidx(smb, &idx)) != SMB_SUCCESS) {
		return result;
	}
	if((time_t)idx.time < t) {
		return SMB_ERR_NOT_FOUND;
654 655
	}

656 657 658 659 660 661 662 663
	match_offset = total - 1;
	*match = idx;

	bot = 0;
	top = total-1;
	clearerr(smb->sid_fp);
	while(bot <= top) {
		long idx_offset = (bot + top) / 2;
664
		if(fseek(smb->sid_fp, idx_offset * idxreclen, SEEK_SET) != 0)
665
			return SMB_ERR_SEEK;
666
		if(fread(&idx, 1, sizeof(idx), smb->sid_fp) != sizeof(idx))
667
			return SMB_ERR_READ;
668 669 670 671 672 673 674 675 676
		if((time_t)idx.time < t) {
			bot = idx_offset + 1;
			continue;
		}
		*match = idx;
		match_offset = idx_offset;
		if((time_t)idx.time > t && idx_offset > 0) {
			top = idx_offset - 1;
			continue;
677
		}
678
		break;
679
	}
680
	return match_offset;
681 682
}

683

684 685
/****************************************************************************/
/* Figures out the total length of the header record for 'msg'              */
686
/* Returns length 															*/
687
/****************************************************************************/
688
ulong smb_getmsghdrlen(smbmsg_t* msg)
689 690
{
	int i;
691
	ulong length;
692 693

	/* fixed portion */
694
	length=sizeof(msghdr_t);
695
	/* data fields */
696
	length+=msg->hdr.total_dfields*sizeof(dfield_t);
697 698
	/* header fields */
	for(i=0;i<msg->total_hfields;i++) {
699 700
		length+=sizeof(hfield_t);
		length+=msg->hfield[i].length; 
701
	}
702
	return(length);
703 704 705 706 707 708
}

/****************************************************************************/
/* Figures out the total length of the data buffer for 'msg'                */
/* Returns length															*/
/****************************************************************************/
709
ulong smb_getmsgdatlen(smbmsg_t* msg)
710 711 712 713 714 715 716 717 718
{
	int i;
	ulong length=0L;

	for(i=0;i<msg->hdr.total_dfields;i++)
		length+=msg->dfield[i].length;
	return(length);
}

719 720 721 722
/****************************************************************************/
/* Figures out the total length of the text buffer for 'msg'                */
/* Returns length															*/
/****************************************************************************/
723
ulong smb_getmsgtxtlen(smbmsg_t* msg)
724 725 726 727
{
	int i;
	ulong length=0L;

rswindell's avatar
rswindell committed
728 729 730 731 732
	for(i=0;i<msg->total_hfields;i++) {
		switch(msg->hfield[i].type) {
		case SMB_COMMENT:
		case SMTPSYSMSG:
		case SMB_POLL_ANSWER:
733
			length+=msg->hfield[i].length+2;
rswindell's avatar
rswindell committed
734 735 736
			break;
		}
	}
737 738 739 740 741 742
	for(i=0;i<msg->hdr.total_dfields;i++)
		if(msg->dfield[i].type==TEXT_BODY || msg->dfield[i].type==TEXT_TAIL)
			length+=msg->dfield[i].length;
	return(length);
}

743
static void set_convenience_ptr(smbmsg_t* msg, uint16_t hfield_type, void* hfield_dat)
rswindell's avatar
rswindell committed
744 745 746
{
	switch(hfield_type) {	/* convenience variables */
		case SENDER:
747 748 749
			msg->from=(char*)hfield_dat;
			break; 
		case FORWARDED:
750
			msg->forwarded = TRUE;
rswindell's avatar