imapext-2007

annotate src/osdep/unix/flocksim.c @ 0:ada5e610ab86

imap-2007e
author yuuji@gentei.org
date Mon, 14 Sep 2009 15:17:45 +0900
parents
children
rev   line source
yuuji@0 1 /* ========================================================================
yuuji@0 2 * Copyright 1988-2007 University of Washington
yuuji@0 3 *
yuuji@0 4 * Licensed under the Apache License, Version 2.0 (the "License");
yuuji@0 5 * you may not use this file except in compliance with the License.
yuuji@0 6 * You may obtain a copy of the License at
yuuji@0 7 *
yuuji@0 8 * http://www.apache.org/licenses/LICENSE-2.0
yuuji@0 9 *
yuuji@0 10 *
yuuji@0 11 * ========================================================================
yuuji@0 12 */
yuuji@0 13
yuuji@0 14 /*
yuuji@0 15 * Program: flock emulation via fcntl() locking
yuuji@0 16 *
yuuji@0 17 * Author: Mark Crispin
yuuji@0 18 * Networks and Distributed Computing
yuuji@0 19 * Computing & Communications
yuuji@0 20 * University of Washington
yuuji@0 21 * Administration Building, AG-44
yuuji@0 22 * Seattle, WA 98195
yuuji@0 23 * Internet: MRC@CAC.Washington.EDU
yuuji@0 24 *
yuuji@0 25 * Date: 10 April 2001
yuuji@0 26 * Last Edited: 11 October 2007
yuuji@0 27 */
yuuji@0 28
yuuji@0 29 #undef flock /* name is used as a struct for fcntl */
yuuji@0 30
yuuji@0 31 #ifndef NOFSTATVFS /* thank you, SUN. NOT! */
yuuji@0 32 # ifndef NOFSTATVFS64
yuuji@0 33 # ifndef _LARGEFILE64_SOURCE
yuuji@0 34 # define _LARGEFILE64_SOURCE
yuuji@0 35 # endif /* _LARGEFILE64_SOURCE */
yuuji@0 36 # endif /* NOFSTATVFFS64 */
yuuji@0 37 #include <sys/statvfs.h>
yuuji@0 38 #endif /* NOFSTATVFS */
yuuji@0 39
yuuji@0 40 #ifndef NSIG /* don't know if this can happen */
yuuji@0 41 #define NSIG 32 /* a common maximum */
yuuji@0 42 #endif
yuuji@0 43
yuuji@0 44 /* Emulator for flock() call
yuuji@0 45 * Accepts: file descriptor
yuuji@0 46 * operation bitmask
yuuji@0 47 * Returns: 0 if successful, -1 if failure under BSD conditions
yuuji@0 48 */
yuuji@0 49
yuuji@0 50 int flocksim (int fd,int op)
yuuji@0 51 {
yuuji@0 52 char tmp[MAILTMPLEN];
yuuji@0 53 int logged = 0;
yuuji@0 54 struct stat sbuf;
yuuji@0 55 struct ustat usbuf;
yuuji@0 56 struct flock fl;
yuuji@0 57 /* lock zero bytes at byte 0 */
yuuji@0 58 fl.l_whence = SEEK_SET; fl.l_start = fl.l_len = 0;
yuuji@0 59 fl.l_pid = getpid (); /* shouldn't be necessary */
yuuji@0 60 switch (op & ~LOCK_NB) { /* translate to fcntl() operation */
yuuji@0 61 case LOCK_EX: /* exclusive */
yuuji@0 62 fl.l_type = F_WRLCK;
yuuji@0 63 break;
yuuji@0 64 case LOCK_SH: /* shared */
yuuji@0 65 fl.l_type = F_RDLCK;
yuuji@0 66 break;
yuuji@0 67 case LOCK_UN: /* unlock */
yuuji@0 68 fl.l_type = F_UNLCK;
yuuji@0 69 break;
yuuji@0 70 default: /* default */
yuuji@0 71 errno = EINVAL;
yuuji@0 72 return -1;
yuuji@0 73 }
yuuji@0 74 /* always return success if disabled */
yuuji@0 75 if (mail_parameters (NIL,GET_DISABLEFCNTLLOCK,NIL)) return 0;
yuuji@0 76
yuuji@0 77 /* Make fcntl() locking of NFS files be a no-op the way it is with flock()
yuuji@0 78 * on BSD. This is because the rpc.statd/rpc.lockd daemons don't work very
yuuji@0 79 * well and cause cluster-wide hangs if you exercise them at all. The
yuuji@0 80 * result of this is that you lose the ability to detect shared mail_open()
yuuji@0 81 * on NFS-mounted files. If you are wise, you'll use IMAP instead of NFS
yuuji@0 82 * for mail files.
yuuji@0 83 *
yuuji@0 84 * Sun alleges that it doesn't matter, and that they have fixed all the
yuuji@0 85 * rpc.statd/rpc.lockd bugs. As of October 2006, that is still false.
yuuji@0 86 *
yuuji@0 87 * We need three tests for three major historical variants in SVR4:
yuuji@0 88 * 1) In NFSv2, ustat() would return -1 in f_tinode for NFS.
yuuji@0 89 * 2) When fstatvfs() was introduced with NFSv3, ustat() was "fixed".
yuuji@0 90 * 3) When 64-bit filesystems were introduced, fstatvfs() would return
yuuji@0 91 * EOVERFLOW; you have to use fstatvfs64() even though you don't care
yuuji@0 92 * about any of the affected values.
yuuji@0 93 *
yuuji@0 94 * We can't use fstatfs() because fstatfs():
yuuji@0 95 * . is documented as being deprecated in SVR4.
yuuji@0 96 * . has inconsistent calling conventions (there are two additional int
yuuji@0 97 * arguments on Solaris and I don't know what they do).
yuuji@0 98 * . returns inconsistent statfs structs. On Solaris, the file system type
yuuji@0 99 * is a short called f_fstyp. On AIX, it's an int called f_type that is
yuuji@0 100 * documented as always being 0!
yuuji@0 101 *
yuuji@0 102 * For what it's worth, here's the scoop on fstatfs() elsewhere:
yuuji@0 103 *
yuuji@0 104 * On Linux, the file system type is a long called f_type that has a file
yuuji@0 105 * system type code. A different module (flocklnx.c) uses this because
yuuji@0 106 * some knothead "improved" flock() to return ENOLCK on NFS files instead
yuuji@0 107 * of being a successful no-op. This "improvement" apparently has been
yuuji@0 108 * reverted, but not before it got to many systems in the field.
yuuji@0 109 *
yuuji@0 110 * On BSD, it's a short called either f_otype or f_type that is documented
yuuji@0 111 * as always being zero. Fortunately, BSD has flock() the way it's supposed
yuuji@0 112 * to be, and none of this nonsense is necessary.
yuuji@0 113 */
yuuji@0 114 if (!fstat (fd,&sbuf)) { /* no hope of working if can't fstat()! */
yuuji@0 115 /* Any base type that begins with "nfs" or "afs" is considered to be a
yuuji@0 116 * network filesystem.
yuuji@0 117 */
yuuji@0 118 #ifndef NOFSTATVFS
yuuji@0 119 struct statvfs vsbuf;
yuuji@0 120 #ifndef NOFSTATVFS64
yuuji@0 121 struct statvfs64 vsbuf64;
yuuji@0 122 if (!fstatvfs64 (fd,&vsbuf64) && (vsbuf64.f_basetype[1] == 'f') &&
yuuji@0 123 (vsbuf64.f_basetype[2] == 's') &&
yuuji@0 124 ((vsbuf64.f_basetype[0] == 'n') || (vsbuf64.f_basetype[0] == 'a')))
yuuji@0 125 return 0;
yuuji@0 126 #endif /* NOFSTATVFS64 */
yuuji@0 127 if (!fstatvfs (fd,&vsbuf) && (vsbuf.f_basetype[1] == 'f') &&
yuuji@0 128 (vsbuf.f_basetype[2] == 's') &&
yuuji@0 129 ((vsbuf.f_basetype[0] == 'n') || (vsbuf.f_basetype[0] == 'a')))
yuuji@0 130 return 0;
yuuji@0 131 #endif /* NOFSTATVFS */
yuuji@0 132 if (!ustat (sbuf.st_dev,&usbuf) && !++usbuf.f_tinode) return 0;
yuuji@0 133 }
yuuji@0 134
yuuji@0 135 /* do the lock */
yuuji@0 136 while (fcntl (fd,(op & LOCK_NB) ? F_SETLK : F_SETLKW,&fl))
yuuji@0 137 if (errno != EINTR) {
yuuji@0 138 /* Can't use switch here because these error codes may resolve to the
yuuji@0 139 * same value on some systems.
yuuji@0 140 */
yuuji@0 141 if ((errno != EWOULDBLOCK) && (errno != EAGAIN) && (errno != EACCES)) {
yuuji@0 142 sprintf (tmp,"Unexpected file locking failure: %.100s",
yuuji@0 143 strerror (errno));
yuuji@0 144 /* give the user a warning of what happened */
yuuji@0 145 MM_NOTIFY (NIL,tmp,WARN);
yuuji@0 146 if (!logged++) syslog (LOG_ERR,"%s",tmp);
yuuji@0 147 if (op & LOCK_NB) return -1;
yuuji@0 148 sleep (5); /* slow things down for loops */
yuuji@0 149 }
yuuji@0 150 /* return failure for non-blocking lock */
yuuji@0 151 else if (op & LOCK_NB) return -1;
yuuji@0 152 }
yuuji@0 153 return 0; /* success */
yuuji@0 154 }
yuuji@0 155
yuuji@0 156 /* Master/slave procedures for safe fcntl() locking.
yuuji@0 157 *
yuuji@0 158 * The purpose of this nonsense is to work around a bad bug in fcntl()
yuuji@0 159 * locking. The cretins who designed it decided that a close() should
yuuji@0 160 * release any locks made by that process on the file opened on that
yuuji@0 161 * file descriptor. Never mind that the lock wasn't made on that file
yuuji@0 162 * descriptor, but rather on some other file descriptor.
yuuji@0 163 *
yuuji@0 164 * This bug is on every implementation of fcntl() locking that I have
yuuji@0 165 * tested. Fortunately, on BSD systems, OSF/1, and Linux, we can use the
yuuji@0 166 * flock() system call which doesn't have this bug.
yuuji@0 167 *
yuuji@0 168 * Note that OSF/1, Linux, and some BSD systems have both broken fcntl()
yuuji@0 169 * locking and the working flock() locking.
yuuji@0 170 *
yuuji@0 171 * The program below can be used to demonstrate this problem. Be sure to
yuuji@0 172 * let it run long enough for all the sleep() calls to finish.
yuuji@0 173 */
yuuji@0 174
yuuji@0 175 #if 0
yuuji@0 176 #include <stdio.h>
yuuji@0 177 #include <unistd.h>
yuuji@0 178 #include <fcntl.h>
yuuji@0 179 #include <errno.h>
yuuji@0 180 #include <string.h>
yuuji@0 181 #include <sys/file.h>
yuuji@0 182
yuuji@0 183 main ()
yuuji@0 184 {
yuuji@0 185 struct flock fl;
yuuji@0 186 int fd,fd2;
yuuji@0 187 char *file = "a.a";
yuuji@0 188 if ((fd = creat (file,0666)) < 0)
yuuji@0 189 perror ("TEST FAILED: can't create test file"),_exit (errno);
yuuji@0 190 close (fd);
yuuji@0 191 if (fork ()) { /* parent */
yuuji@0 192 if ((fd = open (file,O_RDWR,0)) < 0) abort();
yuuji@0 193 /* lock applies to entire file */
yuuji@0 194 fl.l_whence = fl.l_start = fl.l_len = 0;
yuuji@0 195 fl.l_pid = getpid (); /* shouldn't be necessary */
yuuji@0 196 fl.l_type = F_RDLCK;
yuuji@0 197 if (fcntl (fd,F_SETLKW,&fl) == -1) abort ();
yuuji@0 198 sleep (5);
yuuji@0 199 if ((fd2 = open (file,O_RDWR,0)) < 0) abort ();
yuuji@0 200 sleep (1);
yuuji@0 201 puts ("parent test ready -- will hang here if locking works correctly");
yuuji@0 202 close (fd2);
yuuji@0 203 wait (0);
yuuji@0 204 puts ("OS BUG: child terminated");
yuuji@0 205 _exit (0);
yuuji@0 206 }
yuuji@0 207 else { /* child */
yuuji@0 208 sleep (2);
yuuji@0 209 if ((fd = open (file,O_RDWR,0666)) < 0) abort ();
yuuji@0 210 puts ("child test ready -- child will hang if no bug");
yuuji@0 211 /* lock applies to entire file */
yuuji@0 212 fl.l_whence = fl.l_start = fl.l_len = 0;
yuuji@0 213 fl.l_pid = getpid (); /* shouldn't be necessary */
yuuji@0 214 fl.l_type = F_WRLCK;
yuuji@0 215 if (fcntl (fd,F_SETLKW,&fl) == -1) abort ();
yuuji@0 216 puts ("OS BUG: child got lock");
yuuji@0 217 }
yuuji@0 218 }
yuuji@0 219 #endif
yuuji@0 220
yuuji@0 221 /* Beware of systems such as AIX which offer flock() as a compatibility
yuuji@0 222 * function that is just a jacket into fcntl() locking. The program below
yuuji@0 223 * is a variant of the program above, only using flock(). It can be used
yuuji@0 224 * to test to see if your system has real flock() or just a jacket into
yuuji@0 225 * fcntl().
yuuji@0 226 *
yuuji@0 227 * Be sure to let it run long enough for all the sleep() calls to finish.
yuuji@0 228 * If the program hangs, then flock() works and you can dispense with the
yuuji@0 229 * use of this module (you lucky person!).
yuuji@0 230 */
yuuji@0 231
yuuji@0 232 #if 0
yuuji@0 233 #include <stdio.h>
yuuji@0 234 #include <errno.h>
yuuji@0 235 #include <string.h>
yuuji@0 236 #include <sys/file.h>
yuuji@0 237
yuuji@0 238 main ()
yuuji@0 239 {
yuuji@0 240 int fd,fd2;
yuuji@0 241 char *file = "a.a";
yuuji@0 242 if ((fd = creat (file,0666)) < 0)
yuuji@0 243 perror ("TEST FAILED: can't create test file"),_exit (errno);
yuuji@0 244 close (fd);
yuuji@0 245 if (fork ()) { /* parent */
yuuji@0 246 if ((fd = open (file,O_RDWR,0)) < 0) abort();
yuuji@0 247 if (flock (fd,LOCK_SH) == -1) abort ();
yuuji@0 248 sleep (5);
yuuji@0 249 if ((fd2 = open (file,O_RDWR,0)) < 0) abort ();
yuuji@0 250 sleep (1);
yuuji@0 251 puts ("parent test ready -- will hang here if flock() works correctly");
yuuji@0 252 close (fd2);
yuuji@0 253 wait (0);
yuuji@0 254 puts ("OS BUG: child terminated");
yuuji@0 255 _exit (0);
yuuji@0 256 }
yuuji@0 257 else { /* child */
yuuji@0 258 sleep (2);
yuuji@0 259 if ((fd = open (file,O_RDWR,0666)) < 0) abort ();
yuuji@0 260 puts ("child test ready -- child will hang if no bug");
yuuji@0 261 if (flock (fd,LOCK_EX) == -1) abort ();
yuuji@0 262 puts ("OS BUG: child got lock");
yuuji@0 263 }
yuuji@0 264 }
yuuji@0 265 #endif
yuuji@0 266
yuuji@0 267 /* Master/slave details
yuuji@0 268 *
yuuji@0 269 * On broken systems, we invoke an inferior fork to execute any driver
yuuji@0 270 * dispatches which are likely to tickle this bug; specifically, any
yuuji@0 271 * dispatch which may fiddle with a mailbox that is already selected. As
yuuji@0 272 * of this writing, these are: delete, rename, status, scan, copy, and append.
yuuji@0 273 *
yuuji@0 274 * Delete and rename are pretty marginal, yet there are certain clients
yuuji@0 275 * (e.g. Outlook Express) that really want to delete or rename the selected
yuuji@0 276 * mailbox. The same is true of status, but there are people (such as the
yuuji@0 277 * authors of Entourage) who don't understand why status of the selected
yuuji@0 278 * mailbox is bad news.
yuuji@0 279 *
yuuji@0 280 * However, in copy and append it is reasonable to do this to a selected
yuuji@0 281 * mailbox. Although scanning the selected mailbox isn't particularly
yuuji@0 282 * sensible, it's hard to avoid due to wildcards.
yuuji@0 283 *
yuuji@0 284 * It is still possible for an application to trigger the bug by doing
yuuji@0 285 * mail_open() on the same mailbox twice. Don't do it.
yuuji@0 286 *
yuuji@0 287 * Once the slave is invoked, the master only has to read events from the
yuuji@0 288 * slave's output (see below for these events) and translate these events
yuuji@0 289 * to the appropriate c-client callback. When end of file occurs on the pipe,
yuuji@0 290 * the master reads the slave's exit status and uses that as the function
yuuji@0 291 * return. The append master is slightly more complicated because it has to
yuuji@0 292 * send data back to the slave (see below).
yuuji@0 293 *
yuuji@0 294 * The slave takes callback events from the driver which otherwise would
yuuji@0 295 * pass to the main program. Only those events which a slave can actually
yuuji@0 296 * encounter are covered here; for example mm_searched() and mm_list() are
yuuji@0 297 * not covered since a slave never does the operations that trigger these.
yuuji@0 298 * Certain other events (mm_exists(), mm_expunged(), mm_flags()) are discarded
yuuji@0 299 * by the slave since the master will generate these events for itself.
yuuji@0 300 *
yuuji@0 301 * The other events cause the slave to write a newline-terminated string to
yuuji@0 302 * its output. The first character of string indicates the event: S for
yuuji@0 303 * mm_status(), N for mm_notify(), L for mm_log(), C for mm_critical(), X for
yuuji@0 304 * mm_nocritical(), D for mm_diskerror(), F for mm_fatal(), and "A" for append
yuuji@0 305 * argument callback. Most of these events also carry data, which carried as
yuuji@0 306 * text space-delimited in the string.
yuuji@0 307 *
yuuji@0 308 * Append argument callback requires the master to provide the slave with
yuuji@0 309 * data in the slave's input. The first thing that the master provides is
yuuji@0 310 * either a "+" (master has data for the slave) or a "-" (master has no data).
yuuji@0 311 * If the master has data, it will then send the flags, internal date, and
yuuji@0 312 * message text, each as <text octet count><SPACE><text>.
yuuji@0 313 */
yuuji@0 314
yuuji@0 315 /* It should be alright for lockslavep to be a global, since it will always
yuuji@0 316 * be zero in the master (which is where threads would be). The slave won't
yuuji@0 317 * ever thread, since any driver which threads in its methods probably can't
yuuji@0 318 * use fcntl() locking so won't have DR_LOCKING in its driver flags
yuuji@0 319 *
yuuji@0 320 * lockslavep can not be a static, since it's used by the dispatch macros.
yuuji@0 321 */
yuuji@0 322
yuuji@0 323 int lockslavep = 0; /* non-zero means slave process for locking */
yuuji@0 324 static int lockproxycopy = 0; /* non-zero means redo copy as proxy */
yuuji@0 325 FILE *slavein = NIL; /* slave input */
yuuji@0 326 FILE *slaveout = NIL; /* slave output */
yuuji@0 327
yuuji@0 328
yuuji@0 329 /* Common master
yuuji@0 330 * Accepts: permitted stream
yuuji@0 331 * append callback (append calls only, else NIL)
yuuji@0 332 * data for callback (append calls only, else NIL)
yuuji@0 333 * Returns: (master) T if slave succeeded, NIL if slave failed
yuuji@0 334 * (slave) NIL always, with lockslavep non-NIL
yuuji@0 335 */
yuuji@0 336
yuuji@0 337 static long master (MAILSTREAM *stream,append_t af,void *data)
yuuji@0 338 {
yuuji@0 339 MAILSTREAM *st;
yuuji@0 340 MAILSTATUS status;
yuuji@0 341 STRING *message;
yuuji@0 342 FILE *pi,*po;
yuuji@0 343 blocknotify_t bn = (blocknotify_t) mail_parameters (NIL,GET_BLOCKNOTIFY,NIL);
yuuji@0 344 long ret = NIL;
yuuji@0 345 unsigned long i,j;
yuuji@0 346 int c,pid,pipei[2],pipeo[2];
yuuji@0 347 char *s,*t,event[MAILTMPLEN],tmp[MAILTMPLEN];
yuuji@0 348 lockproxycopy = NIL; /* not doing a lock proxycopy */
yuuji@0 349 /* make pipe from slave */
yuuji@0 350 if (pipe (pipei) < 0) mm_log ("Can't create input pipe",ERROR);
yuuji@0 351 else if (pipe (pipeo) < 0) {
yuuji@0 352 mm_log ("Can't create output pipe",ERROR);
yuuji@0 353 close (pipei[0]); close (pipei[1]);
yuuji@0 354 }
yuuji@0 355 else if ((pid = fork ()) < 0) {/* make slave */
yuuji@0 356 mm_log ("Can't create execution process",ERROR);
yuuji@0 357 close (pipei[0]); close (pipei[1]);
yuuji@0 358 close (pipeo[0]); close (pipeo[1]);
yuuji@0 359 }
yuuji@0 360 else if (lockslavep = !pid) { /* are we slave or master? */
yuuji@0 361 alarm (0); /* slave doesn't have alarms or signals */
yuuji@0 362 for (c = 0; c < NSIG; c++) signal (c,SIG_DFL);
yuuji@0 363 if (!(slavein = fdopen (pipeo[0],"r")) ||
yuuji@0 364 !(slaveout = fdopen (pipei[1],"w")))
yuuji@0 365 fatal ("Can't do slave pipe buffered I/O");
yuuji@0 366 close (pipei[0]); /* close parent's side of the pipes */
yuuji@0 367 close (pipeo[1]);
yuuji@0 368 }
yuuji@0 369
yuuji@0 370 else { /* master process */
yuuji@0 371 void *blockdata = (*bn) (BLOCK_SENSITIVE,NIL);
yuuji@0 372 close (pipei[1]); /* close slave's side of the pipes */
yuuji@0 373 close (pipeo[0]);
yuuji@0 374 if (!(pi = fdopen (pipei[0],"r")) || !(po = fdopen (pipeo[1],"w")))
yuuji@0 375 fatal ("Can't do master pipe buffered I/O");
yuuji@0 376 /* do slave events until EOF */
yuuji@0 377 /* read event */
yuuji@0 378 while (fgets (event,MAILTMPLEN-1,pi)) {
yuuji@0 379 if (!(s = strchr (event,'\n'))) {
yuuji@0 380 sprintf (tmp,"Execution process event string too long: %.500s",event);
yuuji@0 381 fatal (tmp);
yuuji@0 382 }
yuuji@0 383 *s = '\0'; /* tie off event at end of line */
yuuji@0 384 switch (event[0]) { /* analyze event */
yuuji@0 385 case 'A': /* append callback */
yuuji@0 386 if ((*af) (NIL,data,&s,&t,&message)) {
yuuji@0 387 if (i = message ? SIZE (message) : 0) {
yuuji@0 388 if (!s) s = ""; /* default values */
yuuji@0 389 if (!t) t = "";
yuuji@0 390 }
yuuji@0 391 else s = t = ""; /* no flags or date if no message */
yuuji@0 392 errno = NIL; /* reset last error */
yuuji@0 393 /* build response */
yuuji@0 394 if (fprintf (po,"+%lu %s%lu %s%lu ",strlen (s),s,strlen (t),t,i) < 0)
yuuji@0 395 fatal ("Failed to pipe append command");
yuuji@0 396 /* write message text */
yuuji@0 397 if (i) do if (putc (c = 0xff & SNX (message),po) == EOF) {
yuuji@0 398 sprintf (tmp,"Failed to pipe %lu bytes (of %lu), last=%u: %.100s",
yuuji@0 399 i,message->size,c,strerror (errno));
yuuji@0 400 fatal (tmp);
yuuji@0 401 } while (--i);
yuuji@0 402 }
yuuji@0 403 else putc ('-',po); /* append error */
yuuji@0 404 fflush (po);
yuuji@0 405 break;
yuuji@0 406 case '&': /* slave wants a proxycopy? */
yuuji@0 407 lockproxycopy = T;
yuuji@0 408 break;
yuuji@0 409
yuuji@0 410 case 'L': /* mm_log() */
yuuji@0 411 i = strtoul (event+1,&s,10);
yuuji@0 412 if (!s || (*s++ != ' ')) {
yuuji@0 413 sprintf (tmp,"Invalid log event arguments: %.500s",event);
yuuji@0 414 fatal (tmp);
yuuji@0 415 }
yuuji@0 416 mm_log (s,i);
yuuji@0 417 break;
yuuji@0 418 case 'N': /* mm_notify() */
yuuji@0 419 st = (MAILSTREAM *) strtoul (event+1,&s,16);
yuuji@0 420 if (s && (*s++ == ' ')) {
yuuji@0 421 i = strtoul (s,&s,10);/* get severity */
yuuji@0 422 if (s && (*s++ == ' ')) {
yuuji@0 423 mm_notify ((st == stream) ? stream : NIL,s,i);
yuuji@0 424 break;
yuuji@0 425 }
yuuji@0 426 }
yuuji@0 427 sprintf (tmp,"Invalid notify event arguments: %.500s",event);
yuuji@0 428 fatal (tmp);
yuuji@0 429
yuuji@0 430 case 'S': /* mm_status() */
yuuji@0 431 st = (MAILSTREAM *) strtoul (event+1,&s,16);
yuuji@0 432 if (s && (*s++ == ' ')) {
yuuji@0 433 status.flags = strtoul (s,&s,10);
yuuji@0 434 if (s && (*s++ == ' ')) {
yuuji@0 435 status.messages = strtoul (s,&s,10);
yuuji@0 436 if (s && (*s++ == ' ')) {
yuuji@0 437 status.recent = strtoul (s,&s,10);
yuuji@0 438 if (s && (*s++ == ' ')) {
yuuji@0 439 status.unseen = strtoul (s,&s,10);
yuuji@0 440 if (s && (*s++ == ' ')) {
yuuji@0 441 status.uidnext = strtoul (s,&s,10);
yuuji@0 442 if (s && (*s++ == ' ')) {
yuuji@0 443 status.uidvalidity = strtoul (s,&s,10);
yuuji@0 444 if (s && (*s++ == ' ')) {
yuuji@0 445 mm_status ((st == stream) ? stream : NIL,s,&status);
yuuji@0 446 break;
yuuji@0 447 }
yuuji@0 448 }
yuuji@0 449 }
yuuji@0 450 }
yuuji@0 451 }
yuuji@0 452 }
yuuji@0 453 }
yuuji@0 454 sprintf (tmp,"Invalid status event arguments: %.500s",event);
yuuji@0 455 fatal (tmp);
yuuji@0 456 case 'C': /* mm_critical() */
yuuji@0 457 st = (MAILSTREAM *) strtoul (event+1,&s,16);
yuuji@0 458 mm_critical ((st == stream) ? stream : NIL);
yuuji@0 459 break;
yuuji@0 460 case 'X': /* mm_nocritical() */
yuuji@0 461 st = (MAILSTREAM *) strtoul (event+1,&s,16);
yuuji@0 462 mm_nocritical ((st == stream) ? stream : NIL);
yuuji@0 463 break;
yuuji@0 464
yuuji@0 465 case 'D': /* mm_diskerror() */
yuuji@0 466 st = (MAILSTREAM *) strtoul (event+1,&s,16);
yuuji@0 467 if (s && (*s++ == ' ')) {
yuuji@0 468 i = strtoul (s,&s,10);
yuuji@0 469 if (s && (*s++ == ' ')) {
yuuji@0 470 j = (long) strtoul (s,NIL,10);
yuuji@0 471 if (st == stream) /* let's hope it's on usable stream */
yuuji@0 472 putc (mm_diskerror (stream,(long) i,j) ? '+' : '-',po);
yuuji@0 473 else if (j) { /* serious diskerror on slave-created stream */
yuuji@0 474 mm_log ("Retrying disk write to avoid mailbox corruption!",WARN);
yuuji@0 475 sleep (5); /* give some time for it to clear up */
yuuji@0 476 putc ('-',po); /* don't abort */
yuuji@0 477 }
yuuji@0 478 else { /* recoverable on slave-created stream */
yuuji@0 479 mm_log ("Error on disk write",ERROR);
yuuji@0 480 putc ('+',po); /* so abort it */
yuuji@0 481 }
yuuji@0 482 fflush (po); /* force it out either way */
yuuji@0 483 break;
yuuji@0 484 }
yuuji@0 485 }
yuuji@0 486 sprintf (tmp,"Invalid diskerror event arguments: %.500s",event);
yuuji@0 487 fatal (tmp);
yuuji@0 488 case 'F': /* mm_fatal() */
yuuji@0 489 mm_fatal (event+1);
yuuji@0 490 break;
yuuji@0 491 default: /* random lossage */
yuuji@0 492 sprintf (tmp,"Unknown event from execution process: %.500s",event);
yuuji@0 493 fatal (tmp);
yuuji@0 494 }
yuuji@0 495 }
yuuji@0 496 fclose (pi); fclose (po); /* done with the pipes */
yuuji@0 497 /* get slave status */
yuuji@0 498 grim_pid_reap_status (pid,NIL,&ret);
yuuji@0 499 if (ret & 0177) { /* signal or stopped */
yuuji@0 500 sprintf (tmp,"Execution process terminated abnormally (%lx)",ret);
yuuji@0 501 mm_log (tmp,ERROR);
yuuji@0 502 ret = NIL;
yuuji@0 503 }
yuuji@0 504 else ret >>= 8; /* return exit code */
yuuji@0 505 (*bn) (BLOCK_NONSENSITIVE,blockdata);
yuuji@0 506 }
yuuji@0 507 return ret; /* return status */
yuuji@0 508 }
yuuji@0 509
yuuji@0 510 /* Safe driver calls */
yuuji@0 511
yuuji@0 512
yuuji@0 513 /* Safely delete mailbox
yuuji@0 514 * Accepts: driver to call under slave
yuuji@0 515 * MAIL stream
yuuji@0 516 * mailbox name to delete
yuuji@0 517 * Returns: T on success, NIL on failure
yuuji@0 518 */
yuuji@0 519
yuuji@0 520 long safe_delete (DRIVER *dtb,MAILSTREAM *stream,char *mbx)
yuuji@0 521 {
yuuji@0 522 long ret = master (stream,NIL,NIL);
yuuji@0 523 if (lockslavep) exit ((*dtb->mbxdel) (stream,mbx));
yuuji@0 524 return ret;
yuuji@0 525 }
yuuji@0 526
yuuji@0 527
yuuji@0 528 /* Safely rename mailbox
yuuji@0 529 * Accepts: driver to call under slave
yuuji@0 530 * MAIL stream
yuuji@0 531 * old mailbox name
yuuji@0 532 * new mailbox name (or NIL for delete)
yuuji@0 533 * Returns: T on success, NIL on failure
yuuji@0 534 */
yuuji@0 535
yuuji@0 536 long safe_rename (DRIVER *dtb,MAILSTREAM *stream,char *old,char *newname)
yuuji@0 537 {
yuuji@0 538 long ret = master (stream,NIL,NIL);
yuuji@0 539 if (lockslavep) exit ((*dtb->mbxren) (stream,old,newname));
yuuji@0 540 return ret;
yuuji@0 541 }
yuuji@0 542
yuuji@0 543
yuuji@0 544 /* Safely get status of mailbox
yuuji@0 545 * Accepts: driver to call under slave
yuuji@0 546 * MAIL stream
yuuji@0 547 * mailbox name
yuuji@0 548 * status flags
yuuji@0 549 * Returns: T on success, NIL on failure
yuuji@0 550 */
yuuji@0 551
yuuji@0 552 long safe_status (DRIVER *dtb,MAILSTREAM *stream,char *mbx,long flags)
yuuji@0 553 {
yuuji@0 554 long ret = master (stream,NIL,NIL);
yuuji@0 555 if (lockslavep) exit ((*dtb->status) (stream,mbx,flags));
yuuji@0 556 return ret;
yuuji@0 557 }
yuuji@0 558
yuuji@0 559
yuuji@0 560 /* Scan file for contents
yuuji@0 561 * Accepts: driver to call under slave
yuuji@0 562 * file name
yuuji@0 563 * desired contents
yuuji@0 564 * length of contents
yuuji@0 565 * length of file
yuuji@0 566 * Returns: NIL if contents not found, T if found
yuuji@0 567 */
yuuji@0 568
yuuji@0 569 long safe_scan_contents (DRIVER *dtb,char *name,char *contents,
yuuji@0 570 unsigned long csiz,unsigned long fsiz)
yuuji@0 571 {
yuuji@0 572 long ret = master (NIL,NIL,NIL);
yuuji@0 573 if (lockslavep) exit (scan_contents (dtb,name,contents,csiz,fsiz));
yuuji@0 574 return ret;
yuuji@0 575 }
yuuji@0 576
yuuji@0 577 /* Safely copy message to mailbox
yuuji@0 578 * Accepts: driver to call under slave
yuuji@0 579 * MAIL stream
yuuji@0 580 * sequence
yuuji@0 581 * destination mailbox
yuuji@0 582 * copy options
yuuji@0 583 * Returns: T if success, NIL if failed
yuuji@0 584 */
yuuji@0 585
yuuji@0 586 long safe_copy (DRIVER *dtb,MAILSTREAM *stream,char *seq,char *mbx,long flags)
yuuji@0 587 {
yuuji@0 588 mailproxycopy_t pc =
yuuji@0 589 (mailproxycopy_t) mail_parameters (stream,GET_MAILPROXYCOPY,NIL);
yuuji@0 590 long ret = master (stream,NIL,NIL);
yuuji@0 591 if (lockslavep) {
yuuji@0 592 /* don't do proxycopy in slave */
yuuji@0 593 if (pc) mail_parameters (stream,SET_MAILPROXYCOPY,(void *) slaveproxycopy);
yuuji@0 594 exit ((*dtb->copy) (stream,seq,mbx,flags));
yuuji@0 595 }
yuuji@0 596 /* do any proxycopy in master */
yuuji@0 597 if (lockproxycopy && pc) return (*pc) (stream,seq,mbx,flags);
yuuji@0 598 return ret;
yuuji@0 599 }
yuuji@0 600
yuuji@0 601
yuuji@0 602 /* Append package for slave */
yuuji@0 603
yuuji@0 604 typedef struct append_data {
yuuji@0 605 int first; /* flag indicating first message */
yuuji@0 606 char *flags; /* message flags */
yuuji@0 607 char *date; /* message date */
yuuji@0 608 char *msg; /* message text */
yuuji@0 609 STRING message; /* message stringstruct */
yuuji@0 610 } APPENDDATA;
yuuji@0 611
yuuji@0 612
yuuji@0 613 /* Safely append message to mailbox
yuuji@0 614 * Accepts: driver to call under slave
yuuji@0 615 * MAIL stream
yuuji@0 616 * destination mailbox
yuuji@0 617 * append callback
yuuji@0 618 * data for callback
yuuji@0 619 * Returns: T if append successful, else NIL
yuuji@0 620 */
yuuji@0 621
yuuji@0 622 long safe_append (DRIVER *dtb,MAILSTREAM *stream,char *mbx,append_t af,
yuuji@0 623 void *data)
yuuji@0 624 {
yuuji@0 625 long ret = master (stream,af,data);
yuuji@0 626 if (lockslavep) {
yuuji@0 627 APPENDDATA ad;
yuuji@0 628 ad.first = T; /* initialize initial append package */
yuuji@0 629 ad.flags = ad.date = ad.msg = NIL;
yuuji@0 630 exit ((*dtb->append) (stream,mbx,slave_append,&ad));
yuuji@0 631 }
yuuji@0 632 return ret;
yuuji@0 633 }
yuuji@0 634
yuuji@0 635 /* Slave callbacks */
yuuji@0 636
yuuji@0 637
yuuji@0 638 /* Message exists (i.e. there are that many messages in the mailbox)
yuuji@0 639 * Accepts: MAIL stream
yuuji@0 640 * message number
yuuji@0 641 */
yuuji@0 642
yuuji@0 643 void slave_exists (MAILSTREAM *stream,unsigned long number)
yuuji@0 644 {
yuuji@0 645 /* this event never passed by slaves */
yuuji@0 646 }
yuuji@0 647
yuuji@0 648
yuuji@0 649 /* Message expunged
yuuji@0 650 * Accepts: MAIL stream
yuuji@0 651 * message number
yuuji@0 652 */
yuuji@0 653
yuuji@0 654 void slave_expunged (MAILSTREAM *stream,unsigned long number)
yuuji@0 655 {
yuuji@0 656 /* this event never passed by slaves */
yuuji@0 657 }
yuuji@0 658
yuuji@0 659
yuuji@0 660 /* Message status changed
yuuji@0 661 * Accepts: MAIL stream
yuuji@0 662 * message number
yuuji@0 663 */
yuuji@0 664
yuuji@0 665 void slave_flags (MAILSTREAM *stream,unsigned long number)
yuuji@0 666 {
yuuji@0 667 /* this event never passed by slaves */
yuuji@0 668 }
yuuji@0 669
yuuji@0 670 /* Mailbox status
yuuji@0 671 * Accepts: MAIL stream
yuuji@0 672 * mailbox name
yuuji@0 673 * mailbox status
yuuji@0 674 */
yuuji@0 675
yuuji@0 676 void slave_status (MAILSTREAM *stream,char *mailbox,MAILSTATUS *status)
yuuji@0 677 {
yuuji@0 678 int i,c;
yuuji@0 679 fprintf (slaveout,"S%lx %lu %lu %lu %lu %lu %lu ",
yuuji@0 680 (unsigned long) stream,status->flags,status->messages,status->recent,
yuuji@0 681 status->unseen,status->uidnext,status->uidvalidity,mailbox);
yuuji@0 682 /* yow! are we paranoid enough yet? */
yuuji@0 683 for (i = 0; (i < 500) && (c = *mailbox++); ++i) switch (c) {
yuuji@0 684 case '\r': case '\n': /* newline in a mailbox name? */
yuuji@0 685 c = ' ';
yuuji@0 686 default:
yuuji@0 687 putc (c,slaveout);
yuuji@0 688 }
yuuji@0 689 putc ('\n',slaveout);
yuuji@0 690 fflush (slaveout);
yuuji@0 691 }
yuuji@0 692
yuuji@0 693 /* Notification event
yuuji@0 694 * Accepts: MAIL stream
yuuji@0 695 * string to log
yuuji@0 696 * error flag
yuuji@0 697 */
yuuji@0 698
yuuji@0 699 void slave_notify (MAILSTREAM *stream,char *string,long errflg)
yuuji@0 700 {
yuuji@0 701 int i,c;
yuuji@0 702 fprintf (slaveout,"N%lx %lu ",(unsigned long) stream,errflg);
yuuji@0 703 /* prevent more than 500 bytes */
yuuji@0 704 for (i = 0; (i < 500) && (c = *string++); ++i) switch (c) {
yuuji@0 705 case '\r': case '\n': /* or embedded newline */
yuuji@0 706 c = ' ';
yuuji@0 707 default:
yuuji@0 708 putc (c,slaveout);
yuuji@0 709 }
yuuji@0 710 putc ('\n',slaveout);
yuuji@0 711 fflush (slaveout);
yuuji@0 712 }
yuuji@0 713
yuuji@0 714
yuuji@0 715 /* Log an event for the user to see
yuuji@0 716 * Accepts: string to log
yuuji@0 717 * error flag
yuuji@0 718 */
yuuji@0 719
yuuji@0 720 void slave_log (char *string,long errflg)
yuuji@0 721 {
yuuji@0 722 int i,c;
yuuji@0 723 fprintf (slaveout,"L%lu ",errflg);
yuuji@0 724 /* prevent more than 500 bytes */
yuuji@0 725 for (i = 0; (i < 500) && (c = *string++); ++i) switch (c) {
yuuji@0 726 case '\r': case '\n': /* or embedded newline */
yuuji@0 727 c = ' ';
yuuji@0 728 default:
yuuji@0 729 putc (c,slaveout);
yuuji@0 730 }
yuuji@0 731 putc ('\n',slaveout);
yuuji@0 732 fflush (slaveout);
yuuji@0 733 }
yuuji@0 734
yuuji@0 735 /* About to enter critical code
yuuji@0 736 * Accepts: stream
yuuji@0 737 */
yuuji@0 738
yuuji@0 739 void slave_critical (MAILSTREAM *stream)
yuuji@0 740 {
yuuji@0 741 fprintf (slaveout,"C%lx\n",(unsigned long) stream);
yuuji@0 742 fflush (slaveout);
yuuji@0 743 }
yuuji@0 744
yuuji@0 745
yuuji@0 746 /* About to exit critical code
yuuji@0 747 * Accepts: stream
yuuji@0 748 */
yuuji@0 749
yuuji@0 750 void slave_nocritical (MAILSTREAM *stream)
yuuji@0 751 {
yuuji@0 752 fprintf (slaveout,"X%lx\n",(unsigned long) stream);
yuuji@0 753 fflush (slaveout);
yuuji@0 754 }
yuuji@0 755
yuuji@0 756 /* Disk error found
yuuji@0 757 * Accepts: stream
yuuji@0 758 * system error code
yuuji@0 759 * flag indicating that mailbox may be clobbered
yuuji@0 760 * Returns: abort flag
yuuji@0 761 */
yuuji@0 762
yuuji@0 763 long slave_diskerror (MAILSTREAM *stream,long errcode,long serious)
yuuji@0 764 {
yuuji@0 765 char tmp[MAILTMPLEN];
yuuji@0 766 int c;
yuuji@0 767 long ret = NIL;
yuuji@0 768 fprintf (slaveout,"D%lx %lu %lu\n",(unsigned long) stream,errcode,serious);
yuuji@0 769 fflush (slaveout);
yuuji@0 770 switch (c = getc (slavein)) {
yuuji@0 771 case EOF: /* pipe broken */
yuuji@0 772 slave_fatal ("Pipe broken reading diskerror response");
yuuji@0 773 case '+': /* user wants to abort */
yuuji@0 774 ret = LONGT;
yuuji@0 775 case '-': /* no abort */
yuuji@0 776 break;
yuuji@0 777 default:
yuuji@0 778 sprintf (tmp,"Unknown master response for diskerror: %c",c);
yuuji@0 779 slave_fatal (tmp);
yuuji@0 780 }
yuuji@0 781 return ret;
yuuji@0 782 }
yuuji@0 783
yuuji@0 784
yuuji@0 785 /* Log a fatal error event
yuuji@0 786 * Accepts: string to log
yuuji@0 787 * Does not return
yuuji@0 788 */
yuuji@0 789
yuuji@0 790 void slave_fatal (char *string)
yuuji@0 791 {
yuuji@0 792 int i,c;
yuuji@0 793 syslog (LOG_ALERT,"IMAP toolkit slave process crash: %.500s",string);
yuuji@0 794 putc ('F',slaveout);
yuuji@0 795 /* prevent more than 500 bytes */
yuuji@0 796 for (i = 0; (i < 500) && (c = *string++); ++i) switch (c) {
yuuji@0 797 case '\r': case '\n': /* newline in a mailbox name? */
yuuji@0 798 c = ' ';
yuuji@0 799 default:
yuuji@0 800 putc (c,slaveout);
yuuji@0 801 }
yuuji@0 802 putc ('\n',slaveout);
yuuji@0 803 fflush (slaveout);
yuuji@0 804 abort (); /* die */
yuuji@0 805 }
yuuji@0 806
yuuji@0 807 /* Append read buffer
yuuji@0 808 * Accepts: number of bytes to read
yuuji@0 809 * error message if fails
yuuji@0 810 * Returns: read-in string
yuuji@0 811 */
yuuji@0 812
yuuji@0 813 static char *slave_append_read (unsigned long n,char *error)
yuuji@0 814 {
yuuji@0 815 #if 0
yuuji@0 816 unsigned long i;
yuuji@0 817 #endif
yuuji@0 818 int c;
yuuji@0 819 char *t,tmp[MAILTMPLEN];
yuuji@0 820 char *s = (char *) fs_get (n + 1);
yuuji@0 821 s[n] = '\0';
yuuji@0 822 #if 0
yuuji@0 823 /* This doesn't work on Solaris with GCC. I think that it's a C library
yuuji@0 824 * bug, since the problem only shows up if the application does fread()
yuuji@0 825 * on some other file
yuuji@0 826 */
yuuji@0 827 for (t = s; n && ((i = fread (t,1,n,slavein)); t += i,n -= i);
yuuji@0 828 #else
yuuji@0 829 for (t = s; n && ((c = getc (slavein)) != EOF); *t++ = c,--n);
yuuji@0 830 #endif
yuuji@0 831 if (n) {
yuuji@0 832 sprintf(tmp,"Pipe broken reading %.100s with %lu bytes remaining",error,n);
yuuji@0 833 slave_fatal (tmp);
yuuji@0 834 }
yuuji@0 835 return s;
yuuji@0 836 }
yuuji@0 837
yuuji@0 838 /* Append message callback
yuuji@0 839 * Accepts: MAIL stream
yuuji@0 840 * append data package
yuuji@0 841 * pointer to return initial flags
yuuji@0 842 * pointer to return message internal date
yuuji@0 843 * pointer to return stringstruct of message or NIL to stop
yuuji@0 844 * Returns: T if success (have message or stop), NIL if error
yuuji@0 845 */
yuuji@0 846
yuuji@0 847 long slave_append (MAILSTREAM *stream,void *data,char **flags,char **date,
yuuji@0 848 STRING **message)
yuuji@0 849 {
yuuji@0 850 char tmp[MAILTMPLEN];
yuuji@0 851 unsigned long n;
yuuji@0 852 int c;
yuuji@0 853 APPENDDATA *ad = (APPENDDATA *) data;
yuuji@0 854 /* flush text of previous message */
yuuji@0 855 if (ad->flags) fs_give ((void **) &ad->flags);
yuuji@0 856 if (ad->date) fs_give ((void **) &ad->date);
yuuji@0 857 if (ad->msg) fs_give ((void **) &ad->msg);
yuuji@0 858 *flags = *date = NIL; /* assume no flags or date */
yuuji@0 859 fputs ("A\n",slaveout); /* tell master we're doing append callback */
yuuji@0 860 fflush (slaveout);
yuuji@0 861 switch (c = getc (slavein)) { /* what did master say? */
yuuji@0 862 case '+': /* have message, get size of flags */
yuuji@0 863 for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0'));
yuuji@0 864 if (c != ' ') {
yuuji@0 865 if (c == EOF) sprintf (tmp,"Pipe broken after flag size %lu",n);
yuuji@0 866 sprintf (tmp,"Missing delimiter after flag size %lu: %c",n,c);
yuuji@0 867 slave_fatal (tmp);
yuuji@0 868 }
yuuji@0 869 if (n) *flags = ad->flags = slave_append_read (n,"flags");
yuuji@0 870 /* get size of date */
yuuji@0 871 for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0'));
yuuji@0 872 if (c != ' ') {
yuuji@0 873 if (c == EOF) sprintf (tmp,"Pipe broken after date size %lu",n);
yuuji@0 874 else sprintf (tmp,"Missing delimiter after date size %lu: %c",n,c);
yuuji@0 875 slave_fatal (tmp);
yuuji@0 876 }
yuuji@0 877 if (n) *date = ad->date = slave_append_read (n,"date");
yuuji@0 878 /* get size of message */
yuuji@0 879 for (n = 0; isdigit (c = getc (slavein)); n *= 10, n += (c - '0'));
yuuji@0 880 if (c != ' ') {
yuuji@0 881 if (c == EOF) sprintf (tmp,"Pipe broken after message size %lu",n);
yuuji@0 882 sprintf (tmp,"Missing delimiter after message size %lu: %c",n,c);
yuuji@0 883 slave_fatal (tmp);
yuuji@0 884 }
yuuji@0 885 if (n) { /* make buffer for message */
yuuji@0 886 ad->msg = slave_append_read (n,"message");
yuuji@0 887 /* initialize stringstruct */
yuuji@0 888 INIT (&ad->message,mail_string,(void *) ad->msg,n);
yuuji@0 889 ad->first = NIL; /* no longer first message */
yuuji@0 890 *message = &ad->message; /* return message */
yuuji@0 891 }
yuuji@0 892 else *message = NIL; /* empty message */
yuuji@0 893 return LONGT;
yuuji@0 894 case '-': /* error */
yuuji@0 895 *message = NIL; /* set stop */
yuuji@0 896 break;
yuuji@0 897 case EOF: /* end of file */
yuuji@0 898 slave_fatal ("Pipe broken reading append response");
yuuji@0 899 default: /* unknown event */
yuuji@0 900 sprintf (tmp,"Unknown master response for append: %c",c);
yuuji@0 901 slave_fatal (tmp);
yuuji@0 902 }
yuuji@0 903 return NIL; /* return failure */
yuuji@0 904 }
yuuji@0 905
yuuji@0 906 /* Proxy copy across mailbox formats
yuuji@0 907 * Accepts: mail stream
yuuji@0 908 * sequence to copy on this stream
yuuji@0 909 * destination mailbox
yuuji@0 910 * option flags
yuuji@0 911 * Returns: T if success, else NIL
yuuji@0 912 */
yuuji@0 913
yuuji@0 914 long slaveproxycopy (MAILSTREAM *stream,char *sequence,char *mailbox,
yuuji@0 915 long options)
yuuji@0 916 {
yuuji@0 917 fputs ("&\n",slaveout); /* redo copy as append */
yuuji@0 918 fflush (slaveout);
yuuji@0 919 return NIL; /* failure for now */
yuuji@0 920 }

UW-IMAP'd extensions by yuuji