void shut_down(int code) { /* free interpreter */ if (interp) sieve_interp_free(&interp); /* close backend connection */ if (backend) { backend_disconnect(backend); free(backend); } /* close mailboxes */ mboxlist_close(); mboxlist_done(); /* cleanup */ if (sieved_out) { prot_flush(sieved_out); prot_free(sieved_out); } if (sieved_in) prot_free(sieved_in); if (sieved_logfd != -1) close(sieved_logfd); #ifdef HAVE_SSL tls_shutdown_serverengine(); #endif cyrus_done(); cyrus_reset_stdio(); /* done */ exit(code); }
EXPORTED int smtpclient_close(smtpclient_t **smp) { if (!smp || !*smp) { return 0; } int r = 0; smtpclient_t *sm = *smp; /* Close backend */ backend_disconnect(sm->backend); if (sm->free_context) { r = sm->free_context(sm->backend); } free(sm->backend); sm->backend = NULL; /* Close log */ if (sm->logfd != -1) { close(sm->logfd); } sm->logfd = -1; /* Free internal state */ if (sm->have_exts) { free_hash_table(sm->have_exts, free); free(sm->have_exts); sm->have_exts = NULL; } buf_free(&sm->buf); free(sm->by); free(sm->ret); free(sm->notify); free(sm->authid); buf_free(&sm->resp.text); free(sm); *smp = NULL; return r; }
static int deliver_msg(char *return_path, char *authuser, int ignorequota, char **users, int numusers, char *mailbox) { int r; struct backend *conn; struct lmtp_txn *txn = LMTP_TXN_ALLOC(numusers ? numusers : 1); int j; int ml = 0; /* must have either some users or a mailbox */ if (!numusers && !mailbox) { usage(); } /* connect */ conn = backend_connect(NULL, sockaddr, &lmtp_protocol, "", NULL, NULL); if (!conn) { just_exit("couldn't connect to lmtpd"); } /* setup txn */ txn->from = return_path; txn->auth = authuser; txn->data = deliver_in; txn->isdotstuffed = 0; txn->tempfail_unknown_mailbox = 0; txn->rcpt_num = numusers ? numusers : 1; if (mailbox) ml = strlen(mailbox); if (numusers == 0) { /* just deliver to mailbox 'mailbox' */ const char *BB = config_getstring(IMAPOPT_POSTUSER); txn->rcpt[0].addr = (char *) xmalloc(ml + strlen(BB) + 2); /* xxx leaks! */ sprintf(txn->rcpt[0].addr, "%s+%s", BB, mailbox); txn->rcpt[0].ignorequota = ignorequota; } else { /* setup each recipient */ for (j = 0; j < numusers; j++) { if (mailbox) { size_t ulen; txn->rcpt[j].addr = (char *) xmalloc(strlen(users[j]) + ml + 2); /* find the length of the userid minus the domain */ ulen = strcspn(users[j], "@"); sprintf(txn->rcpt[j].addr, "%.*s+%s", (int) ulen, users[j], mailbox); /* add the domain if we have one */ if (ulen < strlen(users[j])) strcat(txn->rcpt[j].addr, users[j]+ulen); } else { txn->rcpt[j].addr = xstrdup(users[j]); } txn->rcpt[j].ignorequota = ignorequota; } } /* run txn */ r = lmtp_runtxn(conn, txn); /* disconnect */ backend_disconnect(conn); free(conn); /* examine txn for error state */ r = 0; for (j = 0; j < txn->rcpt_num; j++) { switch (txn->rcpt[j].result) { case RCPT_GOOD: break; case RCPT_TEMPFAIL: r = EC_TEMPFAIL; break; case RCPT_PERMFAIL: /* we just need any permanent failure, though we should probably return data from the client-side LMTP info */ printf("%s: %s\n", txn->rcpt[j].addr, error_message(txn->rcpt[j].r)); if (r != EC_TEMPFAIL) { r = EC_DATAERR; } break; } free(txn->rcpt[j].addr); } free(txn); /* return appropriately */ return r; }
int main(int argc, char **argv) { int r = 0; int opt; int lmtpflag = 0; int ignorequota = 0; char *mailboxname = NULL; char *authuser = NULL; char *return_path = NULL; char buf[1024]; char *alt_config = NULL; while ((opt = getopt(argc, argv, "C:df:r:m:a:F:eE:lqD")) != EOF) { switch(opt) { case 'C': /* alt config file */ alt_config = optarg; break; case 'd': /* Ignore -- /bin/mail compatibility flags */ break; case 'D': logdebug = 1; break; case 'r': case 'f': return_path = optarg; break; case 'm': if (mailboxname) { fprintf(stderr, "deliver: multiple -m options\n"); usage(); /* NOTREACHED */ } if (*optarg) mailboxname = optarg; break; case 'a': if (authuser) { fprintf(stderr, "deliver: multiple -a options\n"); usage(); /* NOTREACHED */ } authuser = optarg; break; case 'F': /* set IMAP flag. we no longer support this */ fprintf(stderr,"deliver: 'F' option no longer supported\n"); usage(); break; case 'e': /* duplicate delivery. ignore */ break; case 'E': fprintf(stderr,"deliver: 'E' option no longer supported\n"); usage(); break; case 'l': lmtpflag = 1; break; case 'q': ignorequota = 1; break; default: usage(); /* NOTREACHED */ } } deliver_in = prot_new(0, 0); deliver_out = prot_new(1, 1); prot_setflushonread(deliver_in, deliver_out); prot_settimeout(deliver_in, 300); cyrus_init(alt_config, "deliver", CYRUSINIT_NODB); sockaddr = config_getstring(IMAPOPT_LMTPSOCKET); if (!sockaddr) { strlcpy(buf, config_dir, sizeof(buf)); strlcat(buf, "/socket/lmtp", sizeof(buf)); sockaddr = buf; } if (lmtpflag == 1) { struct backend *conn = init_net(sockaddr); pipe_through(conn); backend_disconnect(conn); free(conn); } else { if (return_path == NULL) { uid_t me = getuid(); struct passwd *p = getpwuid(me); return_path = p->pw_name; } /* deliver to users or global mailbox */ r = deliver_msg(return_path,authuser, ignorequota, argv+optind, argc - optind, mailboxname); } cyrus_done(); return r; }
/* Handle backend state transitions: * * The backend state starts in Initialising and the following transitions are * allowed. * * Initialising -> InitWait -> Connected * \ * \ ^ \ | * \ | \ | * \ | \ | * \ | \ | * \ | \ | * \ | \ | * V | V V * * Closed <-> Closing * * The state argument specifies the eventual state of the backend and the * function transitions to that state via the shortest path. */ static void set_backend_state(struct backend_info *be, enum xenbus_state state) { while (be->state != state) { switch (be->state) { case XenbusStateInitialising: switch (state) { case XenbusStateInitWait: case XenbusStateConnected: case XenbusStateClosing: backend_switch_state(be, XenbusStateInitWait); break; case XenbusStateClosed: backend_switch_state(be, XenbusStateClosed); break; default: BUG(); } break; case XenbusStateClosed: switch (state) { case XenbusStateInitWait: case XenbusStateConnected: backend_switch_state(be, XenbusStateInitWait); break; case XenbusStateClosing: backend_switch_state(be, XenbusStateClosing); break; default: BUG(); } break; case XenbusStateInitWait: switch (state) { case XenbusStateConnected: backend_connect(be); backend_switch_state(be, XenbusStateConnected); break; case XenbusStateClosing: case XenbusStateClosed: backend_switch_state(be, XenbusStateClosing); break; default: BUG(); } break; case XenbusStateConnected: switch (state) { case XenbusStateInitWait: case XenbusStateClosing: case XenbusStateClosed: backend_disconnect(be); backend_switch_state(be, XenbusStateClosing); break; default: BUG(); } break; case XenbusStateClosing: switch (state) { case XenbusStateInitWait: case XenbusStateConnected: case XenbusStateClosed: backend_switch_state(be, XenbusStateClosed); break; default: BUG(); } break; default: BUG(); } } }
static void replica_disconnect(void) { backend_disconnect(sync_backend); }
static void replica_connect(const char *channel) { int wait; struct protoent *proto; sasl_callback_t *cb; int timeout; const char *port, *auth_status = NULL; cb = mysasl_callbacks(NULL, get_config(channel, "sync_authname"), get_config(channel, "sync_realm"), get_config(channel, "sync_password")); /* get the right port */ port = get_config(channel, "sync_port"); if (port) { imap_csync_protocol.service = port; csync_protocol.service = port; } for (wait = 15;; wait *= 2) { sync_backend = backend_connect(sync_backend, servername, &imap_csync_protocol, "", cb, &auth_status, (verbose > 1 ? fileno(stderr) : -1)); if (sync_backend) { if (sync_backend->capability & CAPA_REPLICATION) { /* attach our IMAP tag buffer to our protstreams as userdata */ sync_backend->in->userdata = sync_backend->out->userdata = &tagbuf; break; } else { backend_disconnect(sync_backend); sync_backend = NULL; } } sync_backend = backend_connect(sync_backend, servername, &csync_protocol, "", cb, NULL, (verbose > 1 ? fileno(stderr) : -1)); if (sync_backend || auth_status || connect_once || wait > 1000) break; fprintf(stderr, "Can not connect to server '%s', retrying in %d seconds\n", servername, wait); sleep(wait); } free_callbacks(cb); cb = NULL; if (!sync_backend) { fprintf(stderr, "Can not connect to server '%s'\n", servername); syslog(LOG_ERR, "Can not connect to server '%s'", servername); _exit(1); } /* Disable Nagle's Algorithm => increase throughput * * http://en.wikipedia.org/wiki/Nagle's_algorithm */ if (servername[0] != '/') { if (sync_backend->sock >= 0 && (proto = getprotobyname("tcp")) != NULL) { int on = 1; if (setsockopt(sync_backend->sock, proto->p_proto, TCP_NODELAY, (void *) &on, sizeof(on)) != 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_NODELAY): %m"); } /* turn on TCP keepalive if set */ if (config_getswitch(IMAPOPT_TCP_KEEPALIVE)) { int r; int optval = 1; socklen_t optlen = sizeof(optval); struct protoent *proto = getprotobyname("TCP"); r = setsockopt(sync_backend->sock, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(SO_KEEPALIVE): %m"); } #ifdef TCP_KEEPCNT optval = config_getint(IMAPOPT_TCP_KEEPALIVE_CNT); if (optval) { r = setsockopt(sync_backend->sock, proto->p_proto, TCP_KEEPCNT, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_KEEPCNT): %m"); } } #endif #ifdef TCP_KEEPIDLE optval = config_getint(IMAPOPT_TCP_KEEPALIVE_IDLE); if (optval) { r = setsockopt(sync_backend->sock, proto->p_proto, TCP_KEEPIDLE, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_KEEPIDLE): %m"); } } #endif #ifdef TCP_KEEPINTVL optval = config_getint(IMAPOPT_TCP_KEEPALIVE_INTVL); if (optval) { r = setsockopt(sync_backend->sock, proto->p_proto, TCP_KEEPINTVL, &optval, optlen); if (r < 0) { syslog(LOG_ERR, "unable to setsocketopt(TCP_KEEPINTVL): %m"); } } #endif } } else { syslog(LOG_ERR, "unable to getprotobyname(\"tcp\"): %m"); } } #ifdef HAVE_ZLIB /* Does the backend support compression? */ if (CAPA(sync_backend, CAPA_COMPRESS)) { prot_printf(sync_backend->out, "%s\r\n", sync_backend->prot->u.std.compress_cmd.cmd); prot_flush(sync_backend->out); if (sync_parse_response("COMPRESS", sync_backend->in, NULL)) { if (do_compress) fatal("Failed to enable compression, aborting", EC_SOFTWARE); syslog(LOG_NOTICE, "Failed to enable compression, continuing uncompressed"); } else { prot_setcompress(sync_backend->in); prot_setcompress(sync_backend->out); } } else if (do_compress) fatal("Backend does not support compression, aborting", EC_SOFTWARE); #endif /* links to sockets */ sync_in = sync_backend->in; sync_out = sync_backend->out; if (verbose > 1) { prot_setlog(sync_in, fileno(stderr)); prot_setlog(sync_out, fileno(stderr)); } /* Set inactivity timer */ timeout = config_getint(IMAPOPT_SYNC_TIMEOUT); if (timeout < 3) timeout = 3; prot_settimeout(sync_in, timeout); /* Force use of LITERAL+ so we don't need two way communications */ prot_setisclient(sync_in, 1); prot_setisclient(sync_out, 1); }
static void replica_connect(const char *channel) { int wait; sasl_callback_t *cb; int timeout; const char *port, *auth_status = NULL; cb = mysasl_callbacks(NULL, get_config(channel, "sync_authname"), get_config(channel, "sync_realm"), get_config(channel, "sync_password")); /* get the right port */ port = get_config(channel, "sync_port"); if (port) { imap_csync_protocol.service = port; csync_protocol.service = port; } for (wait = 15;; wait *= 2) { sync_backend = backend_connect(sync_backend, servername, &imap_csync_protocol, "", cb, &auth_status, (verbose > 1 ? fileno(stderr) : -1)); if (sync_backend) { if (sync_backend->capability & CAPA_REPLICATION) { /* attach our IMAP tag buffer to our protstreams as userdata */ sync_backend->in->userdata = sync_backend->out->userdata = &tagbuf; break; } else { backend_disconnect(sync_backend); sync_backend = NULL; } } sync_backend = backend_connect(sync_backend, servername, &csync_protocol, "", cb, NULL, (verbose > 1 ? fileno(stderr) : -1)); if (sync_backend || auth_status || connect_once || wait > 1000) break; fprintf(stderr, "Can not connect to server '%s', retrying in %d seconds\n", servername, wait); sleep(wait); } free_callbacks(cb); cb = NULL; if (!sync_backend) { fprintf(stderr, "Can not connect to server '%s'\n", servername); syslog(LOG_ERR, "Can not connect to server '%s'", servername); _exit(1); } if (servername[0] != '/' && sync_backend->sock >= 0) { tcp_disable_nagle(sync_backend->sock); tcp_enable_keepalive(sync_backend->sock); } #ifdef HAVE_ZLIB /* Does the backend support compression? */ if (CAPA(sync_backend, CAPA_COMPRESS)) { prot_printf(sync_backend->out, "%s\r\n", sync_backend->prot->u.std.compress_cmd.cmd); prot_flush(sync_backend->out); if (sync_parse_response("COMPRESS", sync_backend->in, NULL)) { if (do_compress) fatal("Failed to enable compression, aborting", EC_SOFTWARE); syslog(LOG_NOTICE, "Failed to enable compression, continuing uncompressed"); } else { prot_setcompress(sync_backend->in); prot_setcompress(sync_backend->out); } } else if (do_compress) fatal("Backend does not support compression, aborting", EC_SOFTWARE); #endif /* links to sockets */ sync_in = sync_backend->in; sync_out = sync_backend->out; if (verbose > 1) { prot_setlog(sync_in, fileno(stderr)); prot_setlog(sync_out, fileno(stderr)); } /* Set inactivity timer */ timeout = config_getint(IMAPOPT_SYNC_TIMEOUT); if (timeout < 3) timeout = 3; prot_settimeout(sync_in, timeout); /* Force use of LITERAL+ so we don't need two way communications */ prot_setisclient(sync_in, 1); prot_setisclient(sync_out, 1); }
/* Handle backend state transitions: * * The backend state starts in InitWait and the following transitions are * allowed. * * InitWait -> Connected * * ^ \ | * | \ | * | \ | * | \ | * | \ | * | \ | * | V V * * Closed <-> Closing * * The state argument specifies the eventual state of the backend and the * function transitions to that state via the shortest path. */ static void set_backend_state(struct backend_info *be, enum xenbus_state state) { while (be->state != state) { switch (be->state) { case XenbusStateClosed: switch (state) { case XenbusStateInitWait: case XenbusStateConnected: pr_info("%s: prepare for reconnect\n", be->dev->nodename); backend_switch_state(be, XenbusStateInitWait); break; case XenbusStateClosing: backend_switch_state(be, XenbusStateClosing); break; default: BUG(); } break; case XenbusStateInitWait: switch (state) { case XenbusStateConnected: backend_connect(be); backend_switch_state(be, XenbusStateConnected); break; case XenbusStateClosing: case XenbusStateClosed: backend_switch_state(be, XenbusStateClosing); break; default: BUG(); } break; case XenbusStateConnected: switch (state) { case XenbusStateInitWait: case XenbusStateClosing: case XenbusStateClosed: backend_disconnect(be); backend_switch_state(be, XenbusStateClosing); break; default: BUG(); } break; case XenbusStateClosing: switch (state) { case XenbusStateInitWait: case XenbusStateConnected: case XenbusStateClosed: backend_switch_state(be, XenbusStateClosed); break; default: BUG(); } break; default: BUG(); } } }
static struct backend *restore_connect(const char *servername, struct buf *tagbuf, const struct restore_options *options) { struct backend *backend = NULL; sasl_callback_t *cb; int timeout; const char *auth_status = NULL; cb = mysasl_callbacks(NULL, config_getstring(IMAPOPT_RESTORE_AUTHNAME), config_getstring(IMAPOPT_RESTORE_REALM), config_getstring(IMAPOPT_RESTORE_PASSWORD)); /* try to connect over IMAP */ backend = backend_connect(backend, servername, &imap_csync_protocol, "", cb, &auth_status, (options->verbose > 1 ? fileno(stderr) : -1)); if (backend) { if (backend->capability & CAPA_REPLICATION) { /* attach our IMAP tag buffer to our protstreams as userdata */ backend->in->userdata = backend->out->userdata = tagbuf; } else { backend_disconnect(backend); backend = NULL; } } /* if that didn't work, fall back to csync */ if (!backend) { backend = backend_connect(backend, servername, &csync_protocol, "", cb, NULL, (options->verbose > 1 ? fileno(stderr) : -1)); } free_callbacks(cb); cb = NULL; if (!backend) { fprintf(stderr, "Can not connect to server '%s'\n", servername); syslog(LOG_ERR, "Can not connect to server '%s'", servername); return NULL; } if (servername[0] != '/' && backend->sock >= 0) { tcp_disable_nagle(backend->sock); tcp_enable_keepalive(backend->sock); } #ifdef HAVE_ZLIB /* Does the backend support compression? */ if (CAPA(backend, CAPA_COMPRESS)) { prot_printf(backend->out, "%s\r\n", backend->prot->u.std.compress_cmd.cmd); prot_flush(backend->out); if (sync_parse_response("COMPRESS", backend->in, NULL)) { if (options->require_compression) fatal("Failed to enable compression, aborting", EX_SOFTWARE); syslog(LOG_NOTICE, "Failed to enable compression, continuing uncompressed"); } else { prot_setcompress(backend->in); prot_setcompress(backend->out); } } else if (options->require_compression) { fatal("Backend does not support compression, aborting", EX_SOFTWARE); } #endif if (options->verbose > 1) { /* XXX did we do this during backend_connect already? */ prot_setlog(backend->in, fileno(stderr)); prot_setlog(backend->out, fileno(stderr)); } /* Set inactivity timer */ timeout = config_getint(IMAPOPT_SYNC_TIMEOUT); if (timeout < 3) timeout = 3; prot_settimeout(backend->in, timeout); /* Force use of LITERAL+ so we don't need two way communications */ prot_setisclient(backend->in, 1); prot_setisclient(backend->out, 1); return backend; }
int main(int argc, char **argv) { save_argv0(argv[0]); const char *alt_config = NULL; const char *input_file = NULL; const char *backup_name = NULL; const char *servername = NULL; enum restore_mode mode = RESTORE_MODE_UNSPECIFIED; int local_only = 0; int wait = 0; int do_nothing = 0; int do_all_mailboxes = 0; struct restore_options options = {0}; options.expunged_mode = RESTORE_EXPUNGED_OKAY; options.trim_deletedprefix = 1; struct backup *backup = NULL; mbname_t *mbname = NULL; struct backup_mailbox_list *mailbox_list = NULL; struct sync_folder_list *reserve_folder_list = NULL; struct sync_reserve_list *reserve_list = NULL; struct buf tagbuf = BUF_INITIALIZER; struct backend *backend = NULL; struct dlist *upload = NULL; int opt, r; while ((opt = getopt(argc, argv, ":A:C:DF:LM:P:UXaf:m:nru:vw:xz")) != EOF) { switch (opt) { case 'A': if (options.keep_uidvalidity) usage(); options.override_acl = optarg; break; case 'C': alt_config = optarg; break; case 'D': /* XXX does this clash with keep_uidvalidity? */ options.trim_deletedprefix = 0; break; case 'F': if (do_all_mailboxes) usage(); input_file = optarg; break; case 'L': local_only = 1; break; case 'M': if (options.keep_uidvalidity) usage(); options.override_mboxname = optarg; break; case 'P': if (options.keep_uidvalidity) usage(); options.override_partition = optarg; break; case 'U': if (options.override_acl || options.override_mboxname || options.override_partition) usage(); options.keep_uidvalidity = 1; break; case 'X': if (options.expunged_mode != RESTORE_EXPUNGED_OKAY) usage(); options.expunged_mode = RESTORE_EXPUNGED_EXCLUDE; break; case 'a': if (input_file) usage(); do_all_mailboxes = 1; break; case 'f': if (mode != RESTORE_MODE_UNSPECIFIED) usage(); mode = RESTORE_MODE_FILENAME; backup_name = optarg; break; case 'm': if (mode != RESTORE_MODE_UNSPECIFIED) usage(); mode = RESTORE_MODE_MBOXNAME; backup_name = optarg; break; case 'n': do_nothing = 1; break; case 'r': options.do_submailboxes = 1; break; case 'u': if (mode != RESTORE_MODE_UNSPECIFIED) usage(); mode = RESTORE_MODE_USERNAME; backup_name = optarg; break; case 'v': options.verbose++; break; case 'w': wait = atoi(optarg); if (wait < 0) usage(); break; case 'x': if (options.expunged_mode != RESTORE_EXPUNGED_OKAY) usage(); options.expunged_mode = RESTORE_EXPUNGED_ONLY; break; case 'z': options.require_compression = 1; break; case ':': if (optopt == 'A') options.override_acl = ""; else usage(); break; default: usage(); break; } } /* we need a server name */ if (optind == argc) usage(); servername = argv[optind++]; /* we need a source of backup data */ if (mode == RESTORE_MODE_UNSPECIFIED) { if (optind == argc) usage(); backup_name = argv[optind++]; mode = RESTORE_MODE_USERNAME; } /* we need either an input file or some objects to restore */ if (!do_all_mailboxes && !input_file && optind == argc) usage(); /* and we can't have both because i said */ if ((input_file || do_all_mailboxes) && optind < argc) usage(); /* okay, arguments seem sane, we are go */ cyrus_init(alt_config, "restore", 0, 0); /* load the SASL plugins */ global_sasl_init(1, 0, mysasl_cb); /* wait here for gdb attach */ if (wait) { fprintf(stderr, "Waiting for %d seconds for gdb attach...\n", wait); sleep(wait); } /* open backup */ switch (mode) { case RESTORE_MODE_FILENAME: r = backup_open_paths(&backup, backup_name, NULL, BACKUP_OPEN_NONBLOCK, BACKUP_OPEN_NOCREATE); break; case RESTORE_MODE_MBOXNAME: mbname = mbname_from_intname(backup_name); if (!mbname) usage(); r = backup_open(&backup, mbname, BACKUP_OPEN_NONBLOCK, BACKUP_OPEN_NOCREATE); mbname_free(&mbname); break; case RESTORE_MODE_USERNAME: mbname = mbname_from_userid(backup_name); if (!mbname) usage(); r = backup_open(&backup, mbname, BACKUP_OPEN_NONBLOCK, BACKUP_OPEN_NOCREATE); mbname_free(&mbname); break; default: usage(); break; } if (r) goto done; /* scan for objects to restore: * mailboxes will have all messages added, modulo expunged_mode * messages will be added individually with appropriate folder */ mailbox_list = xzmalloc(sizeof *mailbox_list); reserve_folder_list = restore_make_reserve_folder_list(backup); reserve_list = sync_reserve_list_create(SYNC_MSGID_LIST_HASH_SIZE); if (input_file) { char buf[MAX_MAILBOX_NAME + 2]; // \n\0 size_t len; FILE *f; if (0 != strcmp(input_file, "-")) { f = fopen(input_file, "r"); if (!f) { fprintf(stderr, "fopen %s: %s\n", input_file, strerror(errno)); goto done;// FIXME shut_down? } } else { f = stdin; } while (fgets(buf, sizeof(buf), f)) { len = strlen(buf); if (len > 0 && buf[len - 1] == '\n') buf[--len] = '\0'; if (len == 0 || buf[0] == '#') continue; r = restore_add_object(buf, &options, backup, mailbox_list, reserve_folder_list, reserve_list); // FIXME r } fclose(f); } else if (do_all_mailboxes) { struct backup_mailbox_list *all_mailboxes; struct backup_mailbox *mailbox; all_mailboxes = backup_get_mailboxes(backup, 0, BACKUP_MAILBOX_ALL_RECORDS); for (mailbox = all_mailboxes->head; mailbox; mailbox = mailbox->next) { restore_add_mailbox(mailbox, &options, mailbox_list, reserve_folder_list, reserve_list); } backup_mailbox_list_empty(all_mailboxes); free(all_mailboxes); } else { int i; for (i = optind; i < argc; i++) { r = restore_add_object(argv[i], &options, backup, mailbox_list, reserve_folder_list, reserve_list); // FIXME r } } if (do_nothing) { if (options.verbose) fprintf(stderr, "do nothing (-n) specified, exiting now\n"); goto done; } /* connect to destination */ backend = restore_connect(servername, &tagbuf, &options); if (!backend) { // FIXME r = -1; goto done; } /* do the restore */ struct sync_reserve *reserve; for (reserve = reserve_list->head; reserve; reserve = reserve->next) { /* send APPLY RESERVE and parse missing lists */ r = sync_reserve_partition(reserve->part, reserve_folder_list, reserve->list, backend); if (r) goto done; /* send APPLY MESSAGEs */ r = backup_prepare_message_upload(backup, reserve->part, reserve->list, &sync_msgid_lookup, &upload); if (r) goto done; /* upload in small(ish) blocks to avoid timeouts */ while (upload->head) { struct dlist *block = dlist_splice(upload, 1024); sync_send_apply(block, backend->out); r = sync_parse_response("MESSAGE", backend->in, NULL); dlist_unlink_files(block); dlist_free(&block); if (r) goto done; } } /* sync_prepare_dlists needs to upload messages per-mailbox, because * it needs the mailbox to find the filename for the message. but * we have no such limitation, so we can upload messages while * looping over the reserve_list instead, above. * * alternatively, if we do it on a per-mailbox basis then we can limit * the hit on the staging directory to only a mailbox worth of messages * at a time. but we don't have a logical grouping that would make * this coherent */ /* send RESTORE MAILBOXes */ struct backup_mailbox *mailbox; for (mailbox = mailbox_list->head; mailbox; mailbox = mailbox->next) { /* XXX filter the mailbox records based on reserve/missing/upload */ /* XXX does this sensibly handle mailbox objs with empty values? */ struct dlist *dl = backup_mailbox_to_dlist(mailbox); if (!dl) continue; if (local_only) { free(dl->name); dl->name = xstrdup("LOCAL_MAILBOX"); } sync_send_restore(dl, backend->out); r = sync_parse_response("MAILBOX", backend->in, NULL); dlist_free(&dl); if (r) goto done; } done: if (r) fprintf(stderr, "%s: %s\n", backup_name, error_message(r)); /* release lock asap */ if (backup) backup_close(&backup); if (backend) backend_disconnect(backend); if (upload) { dlist_unlink_files(upload); dlist_free(&upload); } if (mailbox_list) { backup_mailbox_list_empty(mailbox_list); free(mailbox_list); } if (reserve_folder_list) sync_folder_list_free(&reserve_folder_list); if (reserve_list) sync_reserve_list_free(&reserve_list); buf_free(&tagbuf); backup_cleanup_staging_path(); cyrus_done(); exit(r ? EX_TEMPFAIL : EX_OK); }