/* * Fetch the "mortuary" - a list of dead buddies which we keep around forever * so we can remove them from any client's roster that still has them listed */ void xmpp_store_mortuary(HashList *mortuary) { HashPos *HashPos; long len; void *Value; const char *Key; StrBuf *themsg; themsg = NewStrBuf(); StrBufPrintf(themsg, "Content-type: " XMPPMORTUARY "\n" "Content-transfer-encoding: 7bit\n" "\n" ); HashPos = GetNewHashPos(mortuary, 0); while (GetNextHashPos(mortuary, HashPos, &len, &Key, &Value) != 0) { StrBufAppendPrintf(themsg, "%s\n", (char *)Value); } DeleteHashPos(&HashPos); /* Delete the old mortuary */ CtdlDeleteMessages(USERCONFIGROOM, NULL, 0, XMPPMORTUARY); /* And save the new one to disk */ quickie_message("Citadel", NULL, NULL, USERCONFIGROOM, ChrPtr(themsg), 4, "XMPP Mortuary"); FreeStrBuf(&themsg); }
void save_net_conf(HashList *Nodelist) { char buf[SIZ]; StrBuf *Buf; HashPos *where; void *vNode; NodeConf *Node; const char *Key; long KeyLen; serv_puts("CONF putsys|application/x-citadel-ignet-config"); serv_getln(buf, sizeof buf); if (buf[0] == '4') { if ((Nodelist != NULL) && (GetCount(Nodelist) > 0)) { where = GetNewHashPos(Nodelist, 0); Buf = NewStrBuf(); while (GetNextHashPos(Nodelist, where, &KeyLen, &Key, &vNode)) { Node = (NodeConf*) vNode; if (Node->DeleteMe==0) { SerializeNode(Node, Buf); serv_putbuf(Buf); } } FreeStrBuf(&Buf); DeleteHashPos(&where); } serv_puts("000"); } }
int CountActiveQueueEntries(OneQueItem *MyQItem, int before) { HashPos *It; long len; long ActiveDeliveries; const char *Key; void *vQE; ActiveDeliveries = 0; It = GetNewHashPos(MyQItem->MailQEntries, 0); while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE)) { int Active; MailQEntry *ThisItem = vQE; if (CheckQEntryActive(ThisItem)) { ActiveDeliveries++; Active = 1; } else Active = 0; if (before) ThisItem->Active = Active; else ThisItem->StillActive = Active; } DeleteHashPos(&It); return ActiveDeliveries; }
StrBuf *SerializeQueueItem(OneQueItem *MyQItem) { StrBuf *QMessage; HashPos *It; const char *Key; long len; void *vQE; QMessage = NewStrBufPlain(NULL, SIZ); StrBufPrintf(QMessage, "Content-type: %s\n", SPOOLMIME); // "attempted|%ld\n" "retry|%ld\n",, (long)time(NULL), (long)retry ); StrBufAppendBufPlain(QMessage, HKEY("\nmsgid|"), 0); StrBufAppendPrintf(QMessage, "%ld", MyQItem->MessageID); StrBufAppendBufPlain(QMessage, HKEY("\nsubmitted|"), 0); StrBufAppendPrintf(QMessage, "%ld", MyQItem->Submitted); if (StrLength(MyQItem->BounceTo) > 0) { StrBufAppendBufPlain(QMessage, HKEY("\nbounceto|"), 0); StrBufAppendBuf(QMessage, MyQItem->BounceTo, 0); } if (StrLength(MyQItem->EnvelopeFrom) > 0) { StrBufAppendBufPlain(QMessage, HKEY("\nenvelope_from|"), 0); StrBufAppendBuf(QMessage, MyQItem->EnvelopeFrom, 0); } if (StrLength(MyQItem->SenderRoom) > 0) { StrBufAppendBufPlain(QMessage, HKEY("\nsource_room|"), 0); StrBufAppendBuf(QMessage, MyQItem->SenderRoom, 0); } StrBufAppendBufPlain(QMessage, HKEY("\nretry|"), 0); StrBufAppendPrintf(QMessage, "%ld", MyQItem->Retry); StrBufAppendBufPlain(QMessage, HKEY("\nattempted|"), 0); StrBufAppendPrintf(QMessage, "%ld", time(NULL) /*ctdl_ev_now()*/ + MyQItem->Retry); It = GetNewHashPos(MyQItem->MailQEntries, 0); while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE)) { MailQEntry *ThisItem = vQE; StrBufAppendBufPlain(QMessage, HKEY("\nremote|"), 0); StrBufAppendBuf(QMessage, ThisItem->Recipient, 0); StrBufAppendBufPlain(QMessage, HKEY("|"), 0); StrBufAppendPrintf(QMessage, "%d", ThisItem->Status); StrBufAppendBufPlain(QMessage, HKEY("|"), 0); if (ThisItem->AllStatusMessages != NULL) StrBufAppendBuf(QMessage, ThisItem->AllStatusMessages, 0); else StrBufAppendBuf(QMessage, ThisItem->StatusMessage, 0); } DeleteHashPos(&It); StrBufAppendBufPlain(QMessage, HKEY("\n"), 0); return QMessage; }
static void DBQueueEventAddCallback(EV_P_ ev_async *w, int revents) { CitContext *Ctx; long IOID = -1; long count = 0;; ev_tstamp Now; HashList *q; void *v; HashPos *It; long len; const char *Key; /* get the control command... */ pthread_mutex_lock(&DBEventQueueMutex); if (DBInboundEventQueues[0] == DBInboundEventQueue) { DBInboundEventQueue = DBInboundEventQueues[1]; q = DBInboundEventQueues[0]; } else { DBInboundEventQueue = DBInboundEventQueues[0]; q = DBInboundEventQueues[1]; } pthread_mutex_unlock(&DBEventQueueMutex); Now = ev_now (event_db); It = GetNewHashPos(q, 0); while (GetNextHashPos(q, It, &len, &Key, &v)) { IOAddHandler *h = v; eNextState rc; count ++; if (h->IO->ID == 0) h->IO->ID = EvIDSource++; IOID = h->IO->ID; if (h->IO->StartDB == 0.0) h->IO->StartDB = Now; h->IO->CitContext->lastcmd = h->IO->Now = Now; SetEVState(h->IO, eDBAttach); Ctx = h->IO->CitContext; become_session(Ctx); ev_cleanup_start(event_db, &h->IO->db_abort_by_shutdown); rc = h->EvAttch(h->IO); switch (rc) { case eAbort: ShutDownDBCLient(h->IO); default: break; } } DeleteHashPos(&It); DeleteHashContent(&q); EVQ_syslog(LOG_DEBUG, "%s CC[%ld] DBEVENT Q Add %ld done.", IOSTR, IOID, count); }
eNextState POP3_FetchNetworkUsetableEntry(AsyncIO *IO) { long HKLen; const char *HKey; void *vData; pop3aggr *RecvMsg = (pop3aggr *) IO->Data; SetPOP3State(IO, eUseTable); if((RecvMsg->Pos != NULL) && GetNextHashPos(RecvMsg->MsgNumbers, RecvMsg->Pos, &HKLen, &HKey, &vData)) { if (server_shutting_down) return eAbort; if (CheckIfAlreadySeen("POP3 Item Seen", RecvMsg->CurrMsg->MsgUID, EvGetNow(IO), EvGetNow(IO) - USETABLE_ANTIEXPIRE, eCheckUpdate, IO->ID, CCID) != 0) { /* Item has already been seen */ RecvMsg->CurrMsg->NeedFetch = 0; } else { EVP3CCSM_syslog(LOG_DEBUG, "NO\n"); RecvMsg->CurrMsg->NeedFetch = 1; } return NextDBOperation(&RecvMsg->IO, POP3_FetchNetworkUsetableEntry); } else { /* ok, now we know them all, * continue with reading the actual messages. */ DeleteHashPos(&RecvMsg->Pos); return DBQueueEventContext(IO, POP3_C_ReAttachToFetchMessages); } }
int AnalyseHeaders(ParsedHttpHdrs *Hdr) { OneHttpHeader *pHdr; void *vHdr; long HKLen; const char *HashKey; HashPos *at = GetNewHashPos(Hdr->HTTPHeaders, 0); while (GetNextHashPos(Hdr->HTTPHeaders, at, &HKLen, &HashKey, &vHdr) && (vHdr != NULL)) { pHdr = (OneHttpHeader *)vHdr; if (pHdr->HaveEvaluator) pHdr->H(pHdr->Val, Hdr); } DeleteHashPos(&at); return 0; }
/* * Purge OpenID assocations for missing users (theoretically this will never delete anything) */ int PurgeStaleOpenIDassociations(void) { struct cdbdata *cdboi; struct ctdluser usbuf; HashList *keys = NULL; HashPos *HashPos; char *deleteme = NULL; long len; void *Value; const char *Key; int num_deleted = 0; long usernum = 0L; keys = NewHash(1, NULL); if (!keys) return(0); cdb_rewind(CDB_OPENID); while (cdboi = cdb_next_item(CDB_OPENID), cdboi != NULL) { if (cdboi->len > sizeof(long)) { memcpy(&usernum, cdboi->ptr, sizeof(long)); if (CtdlGetUserByNumber(&usbuf, usernum) != 0) { deleteme = strdup(cdboi->ptr + sizeof(long)), Put(keys, deleteme, strlen(deleteme), deleteme, NULL); } } cdb_free(cdboi); } /* Go through the hash list, deleting keys we stored in it */ HashPos = GetNewHashPos(keys, 0); while (GetNextHashPos(keys, HashPos, &len, &Key, &Value)!=0) { syslog(LOG_DEBUG, "Deleting associated OpenID <%s>", (char*)Value); cdb_delete(CDB_OPENID, Value, strlen(Value)); /* note: don't free(Value) -- deleting the hash list will handle this for us */ ++num_deleted; } DeleteHashPos(&HashPos); DeleteHash(&keys); return num_deleted; }
void cmd_log_get(char *argbuf) { long HKLen; const char *ch; HashPos *Pos; void *vptr; if (CtdlAccessCheck(ac_aide)) return; cprintf("%d Log modules enabled:\n", LISTING_FOLLOWS); Pos = GetNewHashPos(LogDebugEntryTable, 0); while (GetNextHashPos(LogDebugEntryTable, Pos, &HKLen, &ch, &vptr)) { LogDebugEntry *E = (LogDebugEntry*)vptr; cprintf("%s|%d\n", ch, *E->LogP); } DeleteHashPos(&Pos); cprintf("000\n"); }
eNextState POP3C_GetOneMessagID(pop3aggr *RecvMsg) { AsyncIO *IO = &RecvMsg->IO; long HKLen; const char *HKey; void *vData; SetPOP3State(IO, eGetMsgID); #if 0 int rc; rc = TestValidateHash(RecvMsg->MsgNumbers); if (rc != 0) EVP3CCS_syslog(LOG_DEBUG, "Hash Invalid: %d\n", rc); #endif if((RecvMsg->Pos != NULL) && GetNextHashPos(RecvMsg->MsgNumbers, RecvMsg->Pos, &HKLen, &HKey, &vData)) { RecvMsg->CurrMsg = (FetchItem*) vData; /* Find out the UIDL of the message, * to determine whether we've already downloaded it */ StrBufPrintf(RecvMsg->IO.SendBuf.Buf, "UIDL %ld\r\n", RecvMsg->CurrMsg->MSGID); POP3C_DBG_SEND(); } else { RecvMsg->State++; DeleteHashPos(&RecvMsg->Pos); /// done receiving uidls.. start looking them up now. RecvMsg->Pos = GetNewHashPos(RecvMsg->MsgNumbers, 0); return EventQueueDBOperation(&RecvMsg->IO, POP3_FetchNetworkUsetableEntry, 0); } return eReadMore; /* TODO */ }
void DeletePOP3Aggregator(void *vptr) { pop3aggr *ptr = vptr; DeleteHashPos(&ptr->Pos); DeleteHash(&ptr->MsgNumbers); // FreeStrBuf(&ptr->rooms); FreeStrBuf(&ptr->pop3user); FreeStrBuf(&ptr->pop3pass); FreeStrBuf(&ptr->Host); FreeStrBuf(&ptr->RoomName); FreeURL(&ptr->IO.ConnectMe); FreeStrBuf(&ptr->Url); FreeStrBuf(&ptr->IO.IOBuf); FreeStrBuf(&ptr->IO.SendBuf.Buf); FreeStrBuf(&ptr->IO.RecvBuf.Buf); DeleteAsyncMsg(&ptr->IO.ReadMsg); if (((struct CitContext*)ptr->IO.CitContext)) { ((struct CitContext*)ptr->IO.CitContext)->state = CON_IDLE; ((struct CitContext*)ptr->IO.CitContext)->kill_me = 1; } FreeAsyncIOContents(&ptr->IO); free(ptr); }
void RemoveQItem(OneQueItem *MyQItem) { long len; const char* Key; void *VData; HashPos *It; pthread_mutex_lock(&ActiveQItemsLock); It = GetNewHashPos(ActiveQItems, 0); if (GetHashPosFromKey(ActiveQItems, LKEY(MyQItem->MessageID), It)) DeleteEntryFromHash(ActiveQItems, It); else { SMTPC_syslog(LOG_WARNING, "unable to find QItem with ID[%ld]", MyQItem->MessageID); while (GetNextHashPos(ActiveQItems, It, &len, &Key, &VData)) SMTPC_syslog(LOG_WARNING, "have_: ID[%ld]", ((OneQueItem *)VData)->MessageID); } pthread_mutex_unlock(&ActiveQItemsLock); DeleteHashPos(&It); }
/* * Stupidly, XMPP does not specify a way to tell the client to flush its client-side roster * and prepare to receive a new one. So instead we remember every buddy we've ever told the * client about, and push delete operations out at the beginning of a session. * * We omit any users who happen to be online right now, but we still keep them in the mortuary, * which needs to be maintained as a list of every buddy the user has ever seen. We don't know * when they're connecting from the same client and when they're connecting from a different client, * so we have no guarantee of what is in the client side roster at connect time. */ void xmpp_delete_old_buddies_who_no_longer_exist_from_the_client_roster(void) { long len; void *Value; const char *Key; struct CitContext *cptr; int nContexts, i; int online_now = 0; HashList *mortuary = xmpp_fetch_mortuary(); HashPos *HashPos = GetNewHashPos(mortuary, 0); /* we need to omit anyone who is currently online */ cptr = CtdlGetContextArray(&nContexts); /* go through the list of users in the mortuary... */ while (GetNextHashPos(mortuary, HashPos, &len, &Key, &Value) != 0) { online_now = 0; if (cptr) for (i=0; i<nContexts; i++) { if (xmpp_is_visible(&cptr[i], CC)) { if (!strcasecmp(cptr[i].cs_inet_email, (char *)Value)) { online_now = 1; } } } if (!online_now) { xmpp_destroy_buddy((char *)Value, 1); /* aggressive presence update */ } } DeleteHashPos(&HashPos); DeleteHash(&mortuary); free(cptr); }
eNextState FinalizePOP3AggrRun(AsyncIO *IO) { HashPos *It; pop3aggr *cpptr = (pop3aggr *)IO->Data; EVP3C_syslog(LOG_INFO, "%s@%s: fetched %ld new of %d messages in %fs. bye.", ChrPtr(cpptr->pop3user), ChrPtr(cpptr->Host), cpptr->count, GetCount(cpptr->MsgNumbers), IO->Now - cpptr->IOStart ); It = GetNewHashPos(POP3FetchUrls, 0); pthread_mutex_lock(&POP3QueueMutex); { if (GetHashPosFromKey(POP3FetchUrls, SKEY(cpptr->Url), It)) DeleteEntryFromHash(POP3FetchUrls, It); } pthread_mutex_unlock(&POP3QueueMutex); DeleteHashPos(&It); return eAbort; }
/* * Finalize an OpenID authentication */ void cmd_oidf(char *argbuf) { long len; char buf[2048]; char thiskey[1024]; char thisdata[1024]; HashList *keys = NULL; const char *Key; void *Value; ctdl_openid *oiddata = (ctdl_openid *) CC->openid_data; if (CtdlGetConfigInt("c_disable_newu")) { cprintf("%d this system does not support openid.\n", ERROR + CMD_NOT_SUPPORTED); return; } if (oiddata == NULL) { cprintf("%d run OIDS first.\n", ERROR + INTERNAL_ERROR); return; } if (StrLength(oiddata->op_url) == 0){ cprintf("%d No OpenID Endpoint URL has been obtained.\n", ERROR + ILLEGAL_VALUE); return; } keys = NewHash(1, NULL); if (!keys) { cprintf("%d NewHash() failed\n", ERROR + INTERNAL_ERROR); return; } cprintf("%d Transmit OpenID data now\n", START_CHAT_MODE); while (client_getln(buf, sizeof buf), strcmp(buf, "000")) { len = extract_token(thiskey, buf, 0, '|', sizeof thiskey); if (len < 0) { len = sizeof(thiskey) - 1; } extract_token(thisdata, buf, 1, '|', sizeof thisdata); Put(keys, thiskey, len, strdup(thisdata), NULL); } /* Check to see if this is a correct response. * Start with verified=1 but then set it to 0 if anything looks wrong. */ oiddata->verified = 1; char *openid_ns = NULL; if ( (!GetHash(keys, "ns", 2, (void *) &openid_ns)) || (strcasecmp(openid_ns, "http://specs.openid.net/auth/2.0")) ) { syslog(LOG_DEBUG, "This is not an an OpenID assertion"); oiddata->verified = 0; } char *openid_mode = NULL; if ( (!GetHash(keys, "mode", 4, (void *) &openid_mode)) || (strcasecmp(openid_mode, "id_res")) ) { oiddata->verified = 0; } char *openid_claimed_id = NULL; if (GetHash(keys, "claimed_id", 10, (void *) &openid_claimed_id)) { FreeStrBuf(&oiddata->claimed_id); oiddata->claimed_id = NewStrBufPlain(openid_claimed_id, -1); syslog(LOG_DEBUG, "Provider is asserting the Claimed ID '%s'", ChrPtr(oiddata->claimed_id)); } /* Validate the assertion against the server */ syslog(LOG_DEBUG, "Validating..."); CURL *curl; CURLcode res; struct curl_httppost *formpost = NULL; struct curl_httppost *lastptr = NULL; char errmsg[1024] = ""; StrBuf *ReplyBuf = NewStrBuf(); curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "openid.mode", CURLFORM_COPYCONTENTS, "check_authentication", CURLFORM_END ); HashPos *HashPos = GetNewHashPos(keys, 0); while (GetNextHashPos(keys, HashPos, &len, &Key, &Value) != 0) { if (strcasecmp(Key, "mode")) { char k_o_keyname[1024]; snprintf(k_o_keyname, sizeof k_o_keyname, "openid.%s", (const char *)Key); curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, k_o_keyname, CURLFORM_COPYCONTENTS, (char *)Value, CURLFORM_END ); } } DeleteHashPos(&HashPos); curl = ctdl_openid_curl_easy_init(errmsg); curl_easy_setopt(curl, CURLOPT_URL, ChrPtr(oiddata->op_url)); curl_easy_setopt(curl, CURLOPT_WRITEDATA, ReplyBuf); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, CurlFillStrBuf_callback); curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); res = curl_easy_perform(curl); if (res) { syslog(LOG_DEBUG, "cmd_oidf() libcurl error %d: %s", res, errmsg); oiddata->verified = 0; } curl_easy_cleanup(curl); curl_formfree(formpost); /* syslog(LOG_DEBUG, "Validation reply: \n%s", ChrPtr(ReplyBuf)); */ if (cbmstrcasestr(ChrPtr(ReplyBuf), "is_valid:true") == NULL) { oiddata->verified = 0; } FreeStrBuf(&ReplyBuf); syslog(LOG_DEBUG, "OpenID authentication %s", (oiddata->verified ? "succeeded" : "failed") ); /* Respond to the client */ if (oiddata->verified) { /* If we were already logged in, attach the OpenID to the user's account */ if (CC->logged_in) { if (attach_openid(&CC->user, oiddata->claimed_id) == 0) { cprintf("attach\n"); syslog(LOG_DEBUG, "OpenID attach succeeded"); } else { cprintf("fail\n"); syslog(LOG_DEBUG, "OpenID attach failed"); } } /* Otherwise, a user is attempting to log in using the verified OpenID */ else { /* * Existing user who has claimed this OpenID? * * Note: if you think that sending the password back over the wire is insecure, * check your assumptions. If someone has successfully asserted an OpenID that * is associated with the account, they already have password equivalency and can * login, so they could just as easily change the password, etc. */ if (login_via_openid(oiddata->claimed_id) == 0) { cprintf("authenticate\n%s\n%s\n", CC->user.fullname, CC->user.password); logged_in_response(); syslog(LOG_DEBUG, "Logged in using previously claimed OpenID"); } /* * If this system does not allow self-service new user registration, the * remaining modes do not apply, so fail here and now. */ else if (CtdlGetConfigInt("c_disable_newu")) { cprintf("fail\n"); syslog(LOG_DEBUG, "Creating user failed due to local policy"); } /* * New user whose OpenID is verified and Attribute Exchange gave us a name? */ else if (openid_create_user_via_ax(oiddata->claimed_id, keys) == 0) { cprintf("authenticate\n%s\n%s\n", CC->user.fullname, CC->user.password); logged_in_response(); syslog(LOG_DEBUG, "Successfully auto-created new user"); } /* * OpenID is verified, but the desired username either was not specified or * conflicts with an existing user. Manual account creation is required. */ else { char *desired_name = NULL; cprintf("verify_only\n"); cprintf("%s\n", ChrPtr(oiddata->claimed_id)); if (GetHash(keys, "sreg.nickname", 13, (void *) &desired_name)) { cprintf("%s\n", desired_name); } else { cprintf("\n"); } syslog(LOG_DEBUG, "The desired display name is already taken."); } } } else { cprintf("fail\n"); } cprintf("000\n"); if (oiddata->sreg_keys != NULL) { DeleteHash(&oiddata->sreg_keys); oiddata->sreg_keys = NULL; } oiddata->sreg_keys = keys; }
/* * Attempt to auto-create a new Citadel account using the nickname from Attribute Exchange */ int openid_create_user_via_ax(StrBuf *claimed_id, HashList *sreg_keys) { char *nickname = NULL; char *firstname = NULL; char *lastname = NULL; char new_password[32]; long len; const char *Key; void *Value; if (CtdlGetConfigInt("c_auth_mode") != AUTHMODE_NATIVE) return(1); if (CtdlGetConfigInt("c_disable_newu")) return(2); if (CC->logged_in) return(3); HashPos *HashPos = GetNewHashPos(sreg_keys, 0); while (GetNextHashPos(sreg_keys, HashPos, &len, &Key, &Value) != 0) { syslog(LOG_DEBUG, "%s = %s", Key, (char *)Value); if (cbmstrcasestr(Key, "value.nickname") != NULL) { nickname = (char *)Value; } else if ( (nickname == NULL) && (cbmstrcasestr(Key, "value.nickname") != NULL)) { nickname = (char *)Value; } else if (cbmstrcasestr(Key, "value.firstname") != NULL) { firstname = (char *)Value; } else if (cbmstrcasestr(Key, "value.lastname") != NULL) { lastname = (char *)Value; } } DeleteHashPos(&HashPos); if (nickname == NULL) { if ((firstname != NULL) || (lastname != NULL)) { char fullname[1024] = ""; if (firstname) strcpy(fullname, firstname); if (firstname && lastname) strcat(fullname, " "); if (lastname) strcat(fullname, lastname); nickname = fullname; } } if (nickname == NULL) { return(4); } syslog(LOG_DEBUG, "The desired account name is <%s>", nickname); len = cutuserkey(nickname); if (!CtdlGetUser(&CC->user, nickname)) { syslog(LOG_DEBUG, "<%s> is already taken by another user.", nickname); memset(&CC->user, 0, sizeof(struct ctdluser)); return(5); } /* The desired account name is available. Create the account and log it in! */ if (create_user(nickname, len, 1)) return(6); /* Generate a random password. * The user doesn't care what the password is since he is using OpenID. */ snprintf(new_password, sizeof new_password, "%08lx%08lx", random(), random()); CtdlSetPassword(new_password); /* Now attach the verified OpenID to this account. */ attach_openid(&CC->user, claimed_id); return(0); }
/* * smtp_do_bounce() is caled by smtp_do_procmsg() to scan a set of delivery * instructions for "5" codes (permanent fatal errors) and produce/deliver * a "bounce" message (delivery status notification). */ void smtpq_do_bounce(OneQueItem *MyQItem, StrBuf *OMsgTxt, ParsedURL *Relay) { static int seq = 0; struct CtdlMessage *bmsg = NULL; StrBuf *boundary; StrBuf *Msg = NULL; StrBuf *BounceMB; recptypes *valid; time_t now; HashPos *It; void *vQE; long len; const char *Key; int first_attempt = 0; int successful_bounce = 0; int num_bounces = 0; int give_up = 0; SMTPCM_syslog(LOG_DEBUG, "smtp_do_bounce() called\n"); if (MyQItem->SendBounceMail == 0) return; now = time (NULL); //ev_time(); if ( (now - MyQItem->Submitted) > SMTP_GIVE_UP ) { give_up = 1; } if (MyQItem->Retry == SMTP_RETRY_INTERVAL) { first_attempt = 1; } /* * Now go through the instructions checking for stuff. */ Msg = NewStrBufPlain(NULL, 1024); It = GetNewHashPos(MyQItem->MailQEntries, 0); while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE)) { MailQEntry *ThisItem = vQE; if ((ThisItem->Active && (ThisItem->Status == 5)) || /* failed now? */ ((give_up == 1) && (ThisItem->Status != 2)) || ((first_attempt == 1) && (ThisItem->Status != 2))) /* giving up after failed attempts... */ { ++num_bounces; StrBufAppendBufPlain(Msg, HKEY(" "), 0); StrBufAppendBuf(Msg, ThisItem->Recipient, 0); StrBufAppendBufPlain(Msg, HKEY(": "), 0); if (ThisItem->AllStatusMessages != NULL) StrBufAppendBuf(Msg, ThisItem->AllStatusMessages, 0); else StrBufAppendBuf(Msg, ThisItem->StatusMessage, 0); StrBufAppendBufPlain(Msg, HKEY("\r\n"), 0); } } DeleteHashPos(&It); /* Deliver the bounce if there's anything worth mentioning */ SMTPC_syslog(LOG_DEBUG, "num_bounces = %d\n", num_bounces); if (num_bounces == 0) { FreeStrBuf(&Msg); return; } if ((StrLength(MyQItem->SenderRoom) == 0) && MyQItem->HaveRelay) { const char *RelayUrlStr = "[not found]"; /* one message that relaying is broken is enough; no extra room error message. */ StrBuf *RelayDetails = NewStrBuf(); if (Relay != NULL) RelayUrlStr = ChrPtr(Relay->URL); StrBufPrintf(RelayDetails, "Relaying via %s failed permanently. \n Reason:\n%s\n Revalidate your relay configuration.", RelayUrlStr, ChrPtr(Msg)); CtdlAideMessage(ChrPtr(RelayDetails), "Relaying Failed"); FreeStrBuf(&RelayDetails); } boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_")); StrBufAppendPrintf(boundary, "%s_%04x%04x", CtdlGetConfigStr("c_fqdn"), getpid(), ++seq); /* Start building our bounce message; go shopping for memory first. */ BounceMB = NewStrBufPlain( NULL, 1024 + /* mime stuff.... */ StrLength(Msg) + /* the bounce information... */ StrLength(OMsgTxt)); /* the original message */ if (BounceMB == NULL) { FreeStrBuf(&boundary); SMTPCM_syslog(LOG_ERR, "Failed to alloc() bounce message.\n"); return; } bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage)); if (bmsg == NULL) { FreeStrBuf(&boundary); FreeStrBuf(&BounceMB); SMTPCM_syslog(LOG_ERR, "Failed to alloc() bounce message.\n"); return; } memset(bmsg, 0, sizeof(struct CtdlMessage)); StrBufAppendBufPlain(BounceMB, HKEY("Content-type: multipart/mixed; boundary=\""), 0); StrBufAppendBuf(BounceMB, boundary, 0); StrBufAppendBufPlain(BounceMB, HKEY("\"\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("MIME-Version: 1.0\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("X-Mailer: " CITADEL "\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("\r\nThis is a multipart message in MIME format.\r\n\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("--"), 0); StrBufAppendBuf(BounceMB, boundary, 0); StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("Content-type: text/plain\r\n\r\n"), 0); if (give_up) StrBufAppendBufPlain( BounceMB, HKEY( "A message you sent could not be delivered " "to some or all of its recipients\n" "due to prolonged unavailability " "of its destination(s).\n" "Giving up on the following addresses:\n\n" ), 0); else StrBufAppendBufPlain( BounceMB, HKEY( "A message you sent could not be delivered " "to some or all of its recipients.\n" "The following addresses " "were undeliverable:\n\n" ), 0); StrBufAppendBuf(BounceMB, Msg, 0); FreeStrBuf(&Msg); if (StrLength(MyQItem->SenderRoom) > 0) { StrBufAppendBufPlain( BounceMB, HKEY("The message was originaly posted in: "), 0); StrBufAppendBuf(BounceMB, MyQItem->SenderRoom, 0); StrBufAppendBufPlain( BounceMB, HKEY("\n"), 0); } /* Attach the original message */ StrBufAppendBufPlain(BounceMB, HKEY("\r\n--"), 0); StrBufAppendBuf(BounceMB, boundary, 0); StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("Content-type: message/rfc822\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("Content-Transfer-Encoding: 7bit\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("Content-Disposition: inline\r\n"), 0); StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); StrBufAppendBuf(BounceMB, OMsgTxt, 0); /* Close the multipart MIME scope */ StrBufAppendBufPlain(BounceMB, HKEY("--"), 0); StrBufAppendBuf(BounceMB, boundary, 0); StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0); bmsg->cm_magic = CTDLMESSAGE_MAGIC; bmsg->cm_anon_type = MES_NORMAL; bmsg->cm_format_type = FMT_RFC822; CM_SetField(bmsg, eOriginalRoom, HKEY(MAILROOM)); CM_SetField(bmsg, eAuthor, HKEY("Citadel")); CM_SetField(bmsg, eNodeName, CtdlGetConfigStr("c_nodename"), strlen(CtdlGetConfigStr("c_nodename"))); CM_SetField(bmsg, eMsgSubject, HKEY("Delivery Status Notification (Failure)")); CM_SetAsFieldSB(bmsg, eMesageText, &BounceMB); /* First try the user who sent the message */ if (StrLength(MyQItem->BounceTo) == 0) { SMTPCM_syslog(LOG_ERR, "No bounce address specified\n"); } else { SMTPC_syslog(LOG_DEBUG, "bounce to user? <%s>\n", ChrPtr(MyQItem->BounceTo)); } /* Can we deliver the bounce to the original sender? */ valid = validate_recipients(ChrPtr(MyQItem->BounceTo), NULL, 0); if ((valid != NULL) && (valid->num_error == 0)) { CtdlSubmitMsg(bmsg, valid, "", QP_EADDR); successful_bounce = 1; } /* If not, post it in the Aide> room */ if (successful_bounce == 0) { CtdlSubmitMsg(bmsg, NULL, CtdlGetConfigStr("c_aideroom"), QP_EADDR); } /* Free up the memory we used */ free_recipients(valid); FreeStrBuf(&boundary); CM_Free(bmsg); SMTPCM_syslog(LOG_DEBUG, "Done processing bounces\n"); }
void cmd_gvdn(char *argbuf) { const ConfType *pCfg; char *confptr; long min = atol(argbuf); const char *Pos = NULL; const char *PPos = NULL; const char *HKey; long HKLen; StrBuf *Line; StrBuf *Config; StrBuf *Cfg; StrBuf *CfgToken; HashList *List; HashPos *It; void *vptr; List = NewHash(1, NULL); Cfg = NewStrBufPlain(config.c_fqdn, -1); Put(List, SKEY(Cfg), Cfg, HFreeStrBuf); Cfg = NULL; confptr = CtdlGetSysConfig(INTERNETCFG); Config = NewStrBufPlain(confptr, -1); free(confptr); Line = NewStrBufPlain(NULL, StrLength(Config)); CfgToken = NewStrBufPlain(NULL, StrLength(Config)); while (StrBufSipLine(Line, Config, &Pos)) { if (Cfg == NULL) Cfg = NewStrBufPlain(NULL, StrLength(Line)); PPos = NULL; StrBufExtract_NextToken(Cfg, Line, &PPos, '|'); StrBufExtract_NextToken(CfgToken, Line, &PPos, '|'); if (GetHash(CfgNameHash, SKEY(CfgToken), &vptr) && (vptr != NULL)) { pCfg = (ConfType *) vptr; if (pCfg->Type <= min) { Put(List, SKEY(Cfg), Cfg, HFreeStrBuf); Cfg = NULL; } } } cprintf("%d Valid Domains\n", LISTING_FOLLOWS); It = GetNewHashPos(List, 1); while (GetNextHashPos(List, It, &HKLen, &HKey, &vptr)) { cputbuf(vptr); cprintf("\n"); } cprintf("000\n"); DeleteHashPos(&It); DeleteHash(&List); FreeStrBuf(&Cfg); FreeStrBuf(&Line); FreeStrBuf(&CfgToken); FreeStrBuf(&Config); }
/* * smtp_do_procmsg() * * Called by smtp_do_queue() to handle an individual message. */ void smtp_do_procmsg(long msgnum, void *userdata) { time_t now; int mynumsessions = num_sessions; struct CtdlMessage *msg = NULL; char *Author = NULL; char *Address = NULL; char *instr = NULL; StrBuf *PlainQItem; OneQueItem *MyQItem; char *pch; HashPos *It; void *vQE; long len; const char *Key; int HaveBuffers = 0; StrBuf *Msg =NULL; if (mynumsessions > max_sessions_for_outbound_smtp) { SMTPC_syslog(LOG_INFO, "skipping because of num jobs %d > %d max_sessions_for_outbound_smtp", mynumsessions, max_sessions_for_outbound_smtp); } SMTPC_syslog(LOG_DEBUG, "smtp_do_procmsg(%ld)\n", msgnum); ///strcpy(envelope_from, ""); msg = CtdlFetchMessage(msgnum, 1, 1); if (msg == NULL) { SMTPC_syslog(LOG_ERR, "tried %ld but no such message!\n", msgnum); return; } pch = instr = msg->cm_fields[eMesageText]; /* Strip out the headers (no not amd any other non-instruction) line */ while (pch != NULL) { pch = strchr(pch, '\n'); if ((pch != NULL) && ((*(pch + 1) == '\n') || (*(pch + 1) == '\r'))) { instr = pch + 2; pch = NULL; } } PlainQItem = NewStrBufPlain(instr, -1); CM_Free(msg); MyQItem = DeserializeQueueItem(PlainQItem, msgnum); FreeStrBuf(&PlainQItem); if (MyQItem == NULL) { SMTPC_syslog(LOG_ERR, "Msg No %ld: already in progress!\n", msgnum); return; /* s.b. else is already processing... */ } /* * Postpone delivery if we've already tried recently. */ now = time(NULL); if ((MyQItem->ReattemptWhen != 0) && (now < MyQItem->ReattemptWhen) && (run_queue_now == 0)) { SMTPC_syslog(LOG_DEBUG, "Retry time not yet reached. %ld seconds left.", MyQItem->ReattemptWhen - now); It = GetNewHashPos(MyQItem->MailQEntries, 0); pthread_mutex_lock(&ActiveQItemsLock); { if (GetHashPosFromKey(ActiveQItems, LKEY(MyQItem->MessageID), It)) { DeleteEntryFromHash(ActiveQItems, It); } } pthread_mutex_unlock(&ActiveQItemsLock); ////FreeQueItem(&MyQItem); TODO: DeleteEntryFromHash frees this? DeleteHashPos(&It); return; } /* * Bail out if there's no actual message associated with this */ if (MyQItem->MessageID < 0L) { SMTPCM_syslog(LOG_ERR, "no 'msgid' directive found!\n"); It = GetNewHashPos(MyQItem->MailQEntries, 0); pthread_mutex_lock(&ActiveQItemsLock); { if (GetHashPosFromKey(ActiveQItems, LKEY(MyQItem->MessageID), It)) { DeleteEntryFromHash(ActiveQItems, It); } } pthread_mutex_unlock(&ActiveQItemsLock); DeleteHashPos(&It); ////FreeQueItem(&MyQItem); TODO: DeleteEntryFromHash frees this? return; } It = GetNewHashPos(MyQItem->MailQEntries, 0); while (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE)) { MailQEntry *ThisItem = vQE; SMTPC_syslog(LOG_DEBUG, "SMTP Queue: Task: <%s> %d\n", ChrPtr(ThisItem->Recipient), ThisItem->Active); } DeleteHashPos(&It); MyQItem->NotYetShutdownDeliveries = MyQItem->ActiveDeliveries = CountActiveQueueEntries(MyQItem, 1); /* failsafe against overload: * will we exceed the limit set? */ if ((MyQItem->ActiveDeliveries + mynumsessions > max_sessions_for_outbound_smtp) && /* if yes, did we reach more than half of the quota? */ ((mynumsessions * 2) > max_sessions_for_outbound_smtp) && /* if... would we ever fit into half of the quota?? */ (((MyQItem->ActiveDeliveries * 2) < max_sessions_for_outbound_smtp))) { /* abort delivery for another time. */ SMTPC_syslog(LOG_INFO, "SMTP Queue: skipping because of num jobs %d + %ld > %d max_sessions_for_outbound_smtp", mynumsessions, MyQItem->ActiveDeliveries, max_sessions_for_outbound_smtp); It = GetNewHashPos(MyQItem->MailQEntries, 0); pthread_mutex_lock(&ActiveQItemsLock); { if (GetHashPosFromKey(ActiveQItems, LKEY(MyQItem->MessageID), It)) { DeleteEntryFromHash(ActiveQItems, It); } } pthread_mutex_unlock(&ActiveQItemsLock); return; } if (MyQItem->ActiveDeliveries > 0) { ParsedURL *RelayUrls = NULL; int nActivated = 0; int n = MsgCount++; int m = MyQItem->ActiveDeliveries; int i = 1; It = GetNewHashPos(MyQItem->MailQEntries, 0); Msg = smtp_load_msg(MyQItem, n, &Author, &Address); RelayUrls = LoadRelayUrls(MyQItem, Author, Address); if ((RelayUrls == NULL) && MyQItem->HaveRelay) { while ((i <= m) && (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE))) { int KeepBuffers = (i == m); MailQEntry *ThisItem = vQE; StrBufPrintf(ThisItem->StatusMessage, "No relay configured matching %s / %s", (Author != NULL)? Author : "", (Address != NULL)? Address : ""); ThisItem->Status = 5; nActivated++; if (i > 1) n = MsgCount++; SMTPC_syslog(LOG_INFO, "SMTPC: giving up on <%ld> <%s> %d / %d \n", MyQItem->MessageID, ChrPtr(ThisItem->Recipient), i, m); (*((int*) userdata)) ++; smtp_try_one_queue_entry(MyQItem, ThisItem, Msg, KeepBuffers, n, RelayUrls); if (KeepBuffers) HaveBuffers++; i++; } if (Author != NULL) free (Author); if (Address != NULL) free (Address); DeleteHashPos(&It); return; } if (Author != NULL) free (Author); if (Address != NULL) free (Address); while ((i <= m) && (GetNextHashPos(MyQItem->MailQEntries, It, &len, &Key, &vQE))) { MailQEntry *ThisItem = vQE; if (ThisItem->Active == 1) { int KeepBuffers = (i == m); nActivated++; if (nActivated % ndelay_count == 0) usleep(delay_msec); if (i > 1) n = MsgCount++; SMTPC_syslog(LOG_DEBUG, "SMTPC: Trying <%ld> <%s> %d / %d \n", MyQItem->MessageID, ChrPtr(ThisItem->Recipient), i, m); (*((int*) userdata)) ++; smtp_try_one_queue_entry(MyQItem, ThisItem, Msg, KeepBuffers, n, RelayUrls); if (KeepBuffers) HaveBuffers++; i++; } } DeleteHashPos(&It); } else { It = GetNewHashPos(MyQItem->MailQEntries, 0); pthread_mutex_lock(&ActiveQItemsLock); { if (GetHashPosFromKey(ActiveQItems, LKEY(MyQItem->MessageID), It)) { DeleteEntryFromHash(ActiveQItems, It); } else { long len; const char* Key; void *VData; SMTPC_syslog(LOG_WARNING, "unable to find QItem with ID[%ld]", MyQItem->MessageID); while (GetNextHashPos(ActiveQItems, It, &len, &Key, &VData)) { SMTPC_syslog(LOG_WARNING, "have: ID[%ld]", ((OneQueItem *)VData)->MessageID); } } } pthread_mutex_unlock(&ActiveQItemsLock); DeleteHashPos(&It); ////FreeQueItem(&MyQItem); TODO: DeleteEntryFromHash frees this? // TODO: bounce & delete? } if (!HaveBuffers) { FreeStrBuf (&Msg); // TODO : free RelayUrls } }
int CtdlNetworkTalkingTo(const char *nodename, long len, int operation) { int retval = 0; HashPos *Pos = NULL; void *vdata; begin_critical_section(S_NTTLIST); switch(operation) { case NTT_ADD: if (nttlist == NULL) nttlist = NewHash(1, NULL); Put(nttlist, nodename, len, NewStrBufPlain(nodename, len), HFreeStrBuf); if (NTTDebugEnabled) syslog(LOG_DEBUG, "nttlist: added <%s>\n", nodename); break; case NTT_REMOVE: if ((nttlist == NULL) || (GetCount(nttlist) == 0)) break; Pos = GetNewHashPos(nttlist, 1); if (GetHashPosFromKey (nttlist, nodename, len, Pos)) DeleteEntryFromHash(nttlist, Pos); DeleteHashPos(&Pos); if (NTTDebugEnabled) syslog(LOG_DEBUG, "nttlist: removed <%s>\n", nodename); break; case NTT_CHECK: if ((nttlist == NULL) || (GetCount(nttlist) == 0)) break; if (GetHash(nttlist, nodename, len, &vdata)) retval ++; if (NTTDebugEnabled) syslog(LOG_DEBUG, "nttlist: have [%d] <%s>\n", retval, nodename); break; } if (NTTDumpEnabled) { HashPos *It; StrBuf *NTTDump; long len; const char *Key; void *v; NTTDump = NewStrBuf (); It = GetNewHashPos(nttlist, 0); while (GetNextHashPos(nttlist, It, &len, &Key, &v)) { if (StrLength(NTTDump) > 0) StrBufAppendBufPlain(NTTDump, HKEY("|"), 0); StrBufAppendBuf(NTTDump, (StrBuf*) v, 0); } DeleteHashPos(&It); syslog(LOG_DEBUG, "nttlist: Dump: [%d] <%s>\n", GetCount(nttlist), ChrPtr(NTTDump)); FreeStrBuf(&NTTDump); } end_critical_section(S_NTTLIST); return(retval); }
static void QueueEventAddCallback(EV_P_ ev_async *w, int revents) { CitContext *Ctx; long IOID = -1; long count = 0; ev_tstamp Now; HashList *q; void *v; HashPos*It; long len; const char *Key; /* get the control command... */ pthread_mutex_lock(&EventQueueMutex); if (InboundEventQueues[0] == InboundEventQueue) { InboundEventQueue = InboundEventQueues[1]; q = InboundEventQueues[0]; } else { InboundEventQueue = InboundEventQueues[0]; q = InboundEventQueues[1]; } pthread_mutex_unlock(&EventQueueMutex); Now = ev_now (event_base); It = GetNewHashPos(q, 0); while (GetNextHashPos(q, It, &len, &Key, &v)) { IOAddHandler *h = v; count ++; if (h->IO->ID == 0) { h->IO->ID = EvIDSource++; } IOID = h->IO->ID; if (h->IO->StartIO == 0.0) h->IO->StartIO = Now; SetEVState(h->IO, eIOAttach); Ctx = h->IO->CitContext; become_session(Ctx); h->IO->CitContext->lastcmd = h->IO->Now = Now; switch (h->EvAttch(h->IO)) { case eReadMore: case eReadMessage: case eReadFile: case eSendReply: case eSendMore: case eReadPayload: case eSendFile: case eDBQuery: case eSendDNSQuery: case eReadDNSReply: case eConnect: break; case eTerminateConnection: case eAbort: ShutDownCLient(h->IO); break; } } DeleteHashPos(&It); DeleteHashContent(&q); EVQ_syslog(LOG_DEBUG, "%s CC[%ld] EVENT Q Add %ld done.", IOSTR, IOID, count); }
void pop3client_scan(void) { static time_t last_run = 0L; time_t fastest_scan; HashPos *it; long len; const char *Key; void *vrptr; pop3aggr *cptr; become_session(&pop3_client_CC); if (config.c_pop3_fastest < config.c_pop3_fetch) fastest_scan = config.c_pop3_fastest; else fastest_scan = config.c_pop3_fetch; /* * Run POP3 aggregation no more frequently than once every n seconds */ if ( (time(NULL) - last_run) < fastest_scan ) { return; } /* * This is a simple concurrency check to make sure only one pop3client * run is done at a time. We could do this with a mutex, but since we * don't really require extremely fine granularity here, we'll do it * with a static variable instead. */ if (doing_pop3client) return; doing_pop3client = 1; EVP3CQM_syslog(LOG_DEBUG, "pop3client started"); CtdlForEachNetCfgRoom(pop3client_scan_room, NULL, pop3client); pthread_mutex_lock(&POP3QueueMutex); it = GetNewHashPos(POP3FetchUrls, 0); while (!server_shutting_down && GetNextHashPos(POP3FetchUrls, it, &len, &Key, &vrptr) && (vrptr != NULL)) { cptr = (pop3aggr *)vrptr; if (cptr->RefCount == 0) if (!pop3_do_fetching(cptr)) DeletePOP3Aggregator(cptr);////TODO /* if ((palist->interval && time(NULL) > (last_run + palist->interval)) || (time(NULL) > last_run + config.c_pop3_fetch)) pop3_do_fetching(palist->roomname, palist->pop3host, palist->pop3user, palist->pop3pass, palist->keep); pptr = palist; palist = palist->next; free(pptr); */ } DeleteHashPos(&it); pthread_mutex_unlock(&POP3QueueMutex); EVP3CQM_syslog(LOG_DEBUG, "pop3client ended"); last_run = time(NULL); doing_pop3client = 0; }