/* * Copyright (c) 1998-2003 Carnegie Mellon University. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. The name "Carnegie Mellon University" must not be used to * endorse or promote products derived from this software without * prior written permission. For permission or any other legal * details, please contact * Office of Technology Transfer * Carnegie Mellon University * 5000 Forbes Avenue * Pittsburgh, PA 15213-3890 * (412) 268-4387, fax: (412) 268-7395 * tech-transfer@andrew.cmu.edu * * 4. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by Computing Services * at Carnegie Mellon University (http://www.cmu.edu/computing/)." * * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* $Id: acappush.c,v 1.16 2003/02/13 20:15:23 rjs3 Exp $ */ #include #ifdef HAVE_UNISTD_H #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xmalloc.h" #include "imapconf.h" #include "acapmbox.h" #include "acappush.h" #include "exitcodes.h" /* if disconnected and can't reconnect right away how long to wait until retrying */ #define RECONNECT_TIME (60) static int debugmode = 0; typedef enum { CONNECTED, DISCONNECTED, LAST_CONNECT_FAILED } connected_t; acapmbox_handle_t *handle; #define HASHSIZE 107 typedef struct hashentry_s { acapmbdata_t data; struct hashentry_s *next; } hashentry_t; typedef struct hashtb_s { hashentry_t *items[HASHSIZE]; int total_exists; } hashtb_t; hashtb_t hashtable; static int hash_simple (const char *str) { int value = 0; int i; if (!str) return 0; for (i = 0; *str; i++) { value ^= (*str++ << ((i & 3)*8)); } return value % HASHSIZE; } void queueitem(acapmbdata_t *item) { int hashval; hashentry_t *newentry; hashentry_t *e; hashval = hash_simple(item->name); /* see if entry for this already exists */ e = hashtable.items[hashval]; while (e!=NULL) { if (strcmp(e->data.name,item->name)==0) break; e=e->next; } if (e!=NULL) { /* overwrite existing */ memcpy(&(e->data), item, sizeof(acapmbdata_t)); } else { /* make new entry */ newentry = xmalloc(sizeof(hashentry_t)); memcpy(&(newentry->data), item, sizeof(acapmbdata_t)); /* insert into hash table */ newentry->next = hashtable.items[hashval]; hashtable.items[hashval] = newentry; hashtable.total_exists++; } } void send_completed_cb(acap_result_t res, void *rock) { acapmbdata_t *item = (acapmbdata_t *)rock; if (res == ACAP_RESULT_OK) { free(item); return; } /* otherwise */ queueitem(item); } int senditem(acap_conn_t *acapconn, acapmbdata_t *item) { int result; char fullname[MAX_MAILBOX_PATH]; acap_cmd_t *cmd; acap_entry_t *newentry; char tmpstr[30]; /* get the entry path */ result = acapmbox_dataset_name(item->name, fullname); if (result) return result; newentry = acap_entry_new(fullname); if (newentry == NULL) return ACAP_NOMEM; /* make and insert all our attributes */ snprintf(tmpstr, sizeof(tmpstr), "%lu", item->uidvalidity); add_attr(newentry->attrs, "mailbox.uidvalidity", tmpstr); snprintf(tmpstr, sizeof(tmpstr), "%lu", item->answered); add_attr(newentry->attrs, "mailbox.answered", tmpstr); snprintf(tmpstr, sizeof(tmpstr), "%lu", item->flagged); add_attr(newentry->attrs, "mailbox.flagged", tmpstr); snprintf(tmpstr, sizeof(tmpstr), "%lu", item->deleted); add_attr(newentry->attrs, "mailbox.deleted", tmpstr); snprintf(tmpstr, sizeof(tmpstr), "%lu", item->exists); add_attr(newentry->attrs, "mailbox.total", tmpstr); /* store data to server */ result = acap_store_entry(acapconn, newentry, &send_completed_cb, item, 0, &cmd); if (result == ACAP_OK) result = acap_process_on_command(acapconn, cmd, NULL); return result; } void sendsomequeued(acap_conn_t *acapconn, int num) { int spot = rand() % HASHSIZE; hashentry_t *e, *next; int result; int lup; if (num > hashtable.total_exists) num = hashtable.total_exists; for (lup=0;lup= HASHSIZE) spot = 0; } /* pop it */ next = hashtable.items[spot]->next; e = hashtable.items[spot]; hashtable.items[spot]=next; hashtable.total_exists--; /* send it */ result = senditem(acapconn, &(e->data)); /* if failure just requeue item and quit */ if (result!=ACAP_OK) { queueitem(&(e->data)); free(e); return; } free(e); } } void fatal(const char *msg, int err) { if (debugmode) fprintf(stderr, "dying with %s %d\n",msg,err); syslog(LOG_CRIT, "%s", msg); syslog(LOG_NOTICE, "exiting"); exit(err); } acap_conn_t *connect_acap(void) { acap_conn_t *acap_conn; handle = acapmbox_get_handle(); if (!handle) return NULL; acap_conn = acapmbox_get_acapconn(handle); return acap_conn; } void disconnect_acap(void) { acapmbox_disconnect(handle); } int main(int argc, char **argv) { int s, len; struct sockaddr_un local; char str[sizeof(acapmbdata_t)]; struct sockaddr_un from; socklen_t fromlen; mode_t oldumask; connected_t connected = DISCONNECTED; int acapsock = -1; fd_set read_set, rset; int nfds; int r; int lup; acap_conn_t *acap_conn = NULL; struct timeval timeout; time_t when_disconnected = 0; pid_t pid; int opt; char *alt_config = NULL; while ((opt = getopt(argc, argv, "C:d")) != EOF) { switch (opt) { case 'C': /* alt config file */ alt_config = optarg; break; case 'd': /* don't fork. debugging mode */ debugmode = 1; break; default: fprintf(stderr, "invalid argument\n"); exit(EC_USAGE); break; } } if (debugmode) { openlog("acappush", LOG_PID, LOG_LOCAL6); } /* timeout for select is 1 minute */ timeout.tv_sec = 60; timeout.tv_usec = 0; config_init(alt_config, "acappush"); acap_init(); /* initialize the hash table */ hashtable.total_exists = 0; for (lup=0;lup nfds) nfds = acapsock + 1; } else { if (debugmode) fprintf(stderr, "Failed to make connection to ACAP server\n"); connected = LAST_CONNECT_FAILED; when_disconnected = time(NULL); } break; case LAST_CONNECT_FAILED: if ( time(NULL) - when_disconnected >= RECONNECT_TIME) { /* try to connect next time */ connected = DISCONNECTED; } break; default: fatal("Bad state exiting",-1); break; } /* check for the next input */ rset = read_set; n = select(nfds, &rset, NULL, NULL, &timeout); if (n < 0 && errno == EAGAIN) continue; if (n < 0 && errno == EINTR) continue; if (n == -1) { /* uh oh */ syslog(LOG_ERR, "select(): %m"); close(s); fatal("select error",-1); } /* read on unix socket */ if (FD_ISSET(s, &rset)) { fromlen = sizeof(from); memset(str,'\0',sizeof(str)); n = recvfrom(s, str, sizeof(str), 0, (struct sockaddr *) &from, &fromlen); str[n] = '\0'; switch(connected) { case CONNECTED: if (hashtable.total_exists > 0) { queueitem((acapmbdata_t *) str); sendsomequeued(acap_conn, 5); } else { if (senditem(acap_conn,(acapmbdata_t *)str)!=ACAP_OK) queueitem((acapmbdata_t *) str); } break; default: queueitem((acapmbdata_t *)str); break; } } else if ((connected == CONNECTED) && (hashtable.total_exists > 0)) { sendsomequeued(acap_conn, 5); } else { /* log some sort of error */ } } /* never gets here */ exit(1); }