/* * Wrapper for curl_easy_init() that includes the options common to all calls * used in this module. */ CURL *ctdl_openid_curl_easy_init(char *errmsg) { CURL *curl; curl = curl_easy_init(); if (!curl) { return(curl); } curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0); if (errmsg) { curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, errmsg); } curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); #ifdef CURLOPT_HTTP_CONTENT_DECODING curl_easy_setopt(curl, CURLOPT_HTTP_CONTENT_DECODING, 1); curl_easy_setopt(curl, CURLOPT_ENCODING, ""); #endif curl_easy_setopt(curl, CURLOPT_USERAGENT, CITADEL); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30); /* die after 30 seconds */ if ( (!IsEmptyStr(CtdlGetConfigStr("c_ip_addr"))) && (strcmp(CtdlGetConfigStr("c_ip_addr"), "*")) && (strcmp(CtdlGetConfigStr("c_ip_addr"), "::")) && (strcmp(CtdlGetConfigStr("c_ip_addr"), "0.0.0.0")) ) { curl_easy_setopt(curl, CURLOPT_INTERFACE, CtdlGetConfigStr("c_ip_addr")); } return(curl); }
int evcurl_init(AsyncIO *IO) { CURLcode sta; CURL *chnd; EVCURLM_syslog(LOG_DEBUG, "EVCURL: evcurl_init called ms\n"); IO->HttpReq.attached = 0; chnd = IO->HttpReq.chnd = curl_easy_init(); if (!chnd) { EVCURLM_syslog(LOG_ERR, "EVCURL: error initializing curl handle\n"); return 0; } #if DEBUG OPT(VERBOSE, (long)1); #endif OPT(NOPROGRESS, 1L); OPT(NOSIGNAL, 1L); OPT(FAILONERROR, (long)1); OPT(ENCODING, ""); OPT(FOLLOWLOCATION, (long)0); OPT(MAXREDIRS, (long)0); OPT(USERAGENT, CITADEL); OPT(TIMEOUT, (long)1800); OPT(LOW_SPEED_LIMIT, (long)64); OPT(LOW_SPEED_TIME, (long)600); OPT(CONNECTTIMEOUT, (long)600); OPT(PRIVATE, (void *)IO); OPT(FORBID_REUSE, 1); OPT(WRITEFUNCTION, &gotdata); OPT(WRITEDATA, (void *)IO); OPT(ERRORBUFFER, IO->HttpReq.errdesc); if ((!IsEmptyStr(CtdlGetConfigStr("c_ip_addr"))) && (strcmp(CtdlGetConfigStr("c_ip_addr"), "*")) && (strcmp(CtdlGetConfigStr("c_ip_addr"), "::")) && (strcmp(CtdlGetConfigStr("c_ip_addr"), "0.0.0.0")) ) { OPT(INTERFACE, CtdlGetConfigStr("c_ip_addr")); } #ifdef CURLOPT_HTTP_CONTENT_DECODING OPT(HTTP_CONTENT_DECODING, 1); OPT(ENCODING, ""); #endif IO->HttpReq.headers = curl_slist_append(IO->HttpReq.headers, "Connection: close"); return 1; }
/* * cmd_newu() - create a new user account and log in as that user */ void cmd_newu(char *cmdbuf) { int a; long len; char username[SIZ]; if (CtdlGetConfigInt("c_auth_mode") != AUTHMODE_NATIVE) { cprintf("%d This system does not use native mode authentication.\n", ERROR + NOT_HERE); return; } if (CtdlGetConfigInt("c_disable_newu")) { cprintf("%d Self-service user account creation " "is disabled on this system.\n", ERROR + NOT_HERE); return; } if (CC->logged_in) { cprintf("%d Already logged in.\n", ERROR + ALREADY_LOGGED_IN); return; } if (CC->nologin) { cprintf("%d %s: Too many users are already online (maximum is %d)\n", ERROR + MAX_SESSIONS_EXCEEDED, CtdlGetConfigStr("c_nodename"), CtdlGetConfigInt("c_maxsessions")); return; } extract_token(username, cmdbuf, 0, '|', sizeof username); strproc(username); len = cutuserkey(username); if (IsEmptyStr(username)) { cprintf("%d You must supply a user name.\n", ERROR + USERNAME_REQUIRED); return; } if ((!strcasecmp(username, "bbs")) || (!strcasecmp(username, "new")) || (!strcasecmp(username, "."))) { cprintf("%d '%s' is an invalid login name.\n", ERROR + ILLEGAL_VALUE, username); return; } a = create_user(username, len, 1); if (a == 0) { logged_in_response(); } else if (a == ERROR + ALREADY_EXISTS) { cprintf("%d '%s' already exists.\n", ERROR + ALREADY_EXISTS, username); return; } else if (a == ERROR + INTERNAL_ERROR) { cprintf("%d Internal error - user record disappeared?\n", ERROR + INTERNAL_ERROR); return; } else { cprintf("%d unknown error\n", ERROR + INTERNAL_ERROR); } }
/* * USER cmd */ void cmd_user(char *cmdbuf) { char username[256]; int a; CON_syslog(LOG_DEBUG, "cmd_user(%s)\n", cmdbuf); extract_token(username, cmdbuf, 0, '|', sizeof username); CON_syslog(LOG_DEBUG, "username: %s\n", username); striplt(username); CON_syslog(LOG_DEBUG, "username: %s\n", username); a = CtdlLoginExistingUser(NULL, username); switch (a) { case login_already_logged_in: cprintf("%d Already logged in.\n", ERROR + ALREADY_LOGGED_IN); return; case login_too_many_users: cprintf("%d %s: " "Too many users are already online " "(maximum is %d)\n", ERROR + MAX_SESSIONS_EXCEEDED, CtdlGetConfigStr("c_nodename"), CtdlGetConfigInt("c_maxsessions")); return; case login_ok: cprintf("%d Password required for %s\n", MORE_DATA, CC->curr_user); return; case login_not_found: cprintf("%d %s not found.\n", ERROR + NO_SUCH_USER, username); return; default: cprintf("%d Internal error\n", ERROR + INTERNAL_ERROR); } }
/* * INVT and KICK commands */ void cmd_invt_kick(char *iuser, int op) { /* * These commands are only allowed by admins, room admins, * and room namespace owners */ if (is_room_aide()) { /* access granted */ } else if ( ((atol(CC->room.QRname) == CC->user.usernum) ) && (CC->user.usernum != 0) ) { /* access granted */ } else { /* access denied */ cprintf("%d Higher access or room ownership required.\n", ERROR + HIGHER_ACCESS_REQUIRED); return; } if (!strncasecmp(CC->room.QRname, CtdlGetConfigStr("c_baseroom"), ROOMNAMELEN)) { cprintf("%d Can't add/remove users from this room.\n", ERROR + NOT_HERE); return; } if (CtdlInvtKick(iuser, op) != 0) { cprintf("%d No such user.\n", ERROR + NO_SUCH_USER); return; } cprintf("%d %s %s %s.\n", CIT_OK, iuser, ((op == 1) ? "invited to" : "kicked out of"), CC->room.QRname); return; }
/* * Deliver list messages to everyone on the list ... efficiently */ void network_deliver_list(struct CtdlMessage *msg, SpoolControl *sc, const char *RoomName) { recptypes *valid; char bounce_to[256]; /* Don't do this if there were no recipients! */ if (sc->Users[listrecp] == NULL) return; /* Now generate the delivery instructions */ /* Where do we want bounces and other noise to be heard? * Surely not the list members! */ snprintf(bounce_to, sizeof bounce_to, "room_aide@%s", CtdlGetConfigStr("c_fqdn")); /* Now submit the message */ valid = validate_recipients(ChrPtr(sc->Users[listrecp]), NULL, 0); if (valid != NULL) { valid->bounce_to = strdup(bounce_to); valid->envelope_from = strdup(bounce_to); valid->sending_room = strdup(RoomName); CtdlSubmitMsg(msg, valid, NULL, 0); free_recipients(valid); } /* Do not call CM_Free(msg) here; the caller will free it. */ }
/* * Deliver digest messages */ void network_deliver_digest(SpoolControl *sc) { struct CitContext *CCC = CC; long len; char buf[SIZ]; char *pbuf; struct CtdlMessage *msg = NULL; long msglen; recptypes *valid; char bounce_to[256]; if (sc->Users[digestrecp] == NULL) return; msg = malloc(sizeof(struct CtdlMessage)); memset(msg, 0, sizeof(struct CtdlMessage)); msg->cm_magic = CTDLMESSAGE_MAGIC; msg->cm_format_type = FMT_RFC822; msg->cm_anon_type = MES_NORMAL; CM_SetFieldLONG(msg, eTimestamp, time(NULL)); CM_SetField(msg, eAuthor, CCC->room.QRname, strlen(CCC->room.QRname)); len = snprintf(buf, sizeof buf, "[%s]", CCC->room.QRname); CM_SetField(msg, eMsgSubject, buf, len); CM_SetField(msg, erFc822Addr, SKEY(sc->Users[roommailalias])); CM_SetField(msg, eRecipient, SKEY(sc->Users[roommailalias])); /* Set the 'List-ID' header */ CM_SetField(msg, eListID, SKEY(sc->ListID)); /* * Go fetch the contents of the digest */ fseek(sc->digestfp, 0L, SEEK_END); msglen = ftell(sc->digestfp); pbuf = malloc(msglen + 1); fseek(sc->digestfp, 0L, SEEK_SET); fread(pbuf, (size_t)msglen, 1, sc->digestfp); pbuf[msglen] = '\0'; CM_SetAsField(msg, eMesageText, &pbuf, msglen); /* Now generate the delivery instructions */ /* Where do we want bounces and other noise to be heard? * Surely not the list members! */ snprintf(bounce_to, sizeof bounce_to, "room_aide@%s", CtdlGetConfigStr("c_fqdn")); /* Now submit the message */ valid = validate_recipients(ChrPtr(sc->Users[digestrecp]), NULL, 0); if (valid != NULL) { valid->bounce_to = strdup(bounce_to); valid->envelope_from = strdup(bounce_to); CtdlSubmitMsg(msg, valid, NULL, 0); } CM_Free(msg); free_recipients(valid); }
eNextState STMPC_send_HELO(SmtpOutMsg *Msg) { AsyncIO *IO = &Msg->IO; StrBufPrintf(Msg->IO.SendBuf.Buf, "HELO %s\r\n", CtdlGetConfigStr("c_fqdn")); SMTP_DBG_SEND(); return eReadMessage; }
void network_process_participate(SpoolControl *sc, struct CtdlMessage *omsg, long *delete_after_send) { struct CtdlMessage *msg = NULL; int ok_to_participate = 0; StrBuf *Buf = NULL; recptypes *valid; /* * Process client-side list participations for this room */ if (sc->Users[participate] == NULL) return; msg = CM_Duplicate(omsg); /* Only send messages which originated on our own * Citadel network, otherwise we'll end up sending the * remote mailing list's messages back to it, which * is rude... */ ok_to_participate = 0; if (!CM_IsEmpty(msg, eNodeName)) { if (!strcasecmp(msg->cm_fields[eNodeName], CtdlGetConfigStr("c_nodename"))) { ok_to_participate = 1; } Buf = NewStrBufPlain(CM_KEY(msg, eNodeName)); if (CtdlIsValidNode(NULL, NULL, Buf, sc->working_ignetcfg, sc->the_netmap) == 0) { ok_to_participate = 1; } } if (ok_to_participate) { /* Replace the Internet email address of the * actual author with the email address of the * room itself, so the remote listserv doesn't * reject us. */ CM_SetField(msg, erFc822Addr, SKEY(sc->Users[roommailalias])); valid = validate_recipients(ChrPtr(sc->Users[participate]) , NULL, 0); CM_SetField(msg, eRecipient, SKEY(sc->Users[roommailalias])); CtdlSubmitMsg(msg, valid, "", 0); free_recipients(valid); } FreeStrBuf(&Buf); CM_Free(msg); }
eNextState SMTPC_send_EHLO(SmtpOutMsg *Msg) { AsyncIO *IO = &Msg->IO; /* At this point we know we are talking to a real SMTP server */ /* Do a EHLO command. If it fails, try the HELO command. */ StrBufPrintf(Msg->IO.SendBuf.Buf, "EHLO %s\r\n", CtdlGetConfigStr("c_fqdn")); SMTP_DBG_SEND(); return eReadMessage; }
/* * Return nonzero if the supplied name is an alias for this host. */ int CtdlHostAlias(char *fqdn) { int config_lines; int i; char buf[256]; char host[256], type[256]; int found = 0; if (fqdn == NULL) return(hostalias_nomatch); if (IsEmptyStr(fqdn)) return(hostalias_nomatch); if (!strcasecmp(fqdn, "localhost")) return(hostalias_localhost); if (!strcasecmp(fqdn, CtdlGetConfigStr("c_fqdn"))) return(hostalias_localhost); if (!strcasecmp(fqdn, CtdlGetConfigStr("c_nodename"))) return(hostalias_localhost); if (inetcfg == NULL) return(hostalias_nomatch); config_lines = num_tokens(inetcfg, '\n'); for (i=0; i<config_lines; ++i) { extract_token(buf, inetcfg, i, '\n', sizeof buf); extract_token(host, buf, 0, '|', sizeof host); extract_token(type, buf, 1, '|', sizeof type); found = 0; /* Process these in a specific order, in case there are multiple matches. * We want directory to override masq, for example. */ if ( (!strcasecmp(type, "masqdomain")) && (!strcasecmp(fqdn, host))) { found = hostalias_masq; } if ( (!strcasecmp(type, "localhost")) && (!strcasecmp(fqdn, host))) { found = hostalias_localhost; } if ( (!strcasecmp(type, "directory")) && (!strcasecmp(fqdn, host))) { found = hostalias_directory; } if (found) return(found); } return(hostalias_nomatch); }
// // Here's where our NNTP session begins its happy day. // void nntp_greeting(void) { strcpy(CC->cs_clientname, "NNTP session"); CC->cs_flags |= CS_STEALTH; CC->session_specific_data = malloc(sizeof(citnntp)); citnntp *nntpstate = (citnntp *) CC->session_specific_data; memset(nntpstate, 0, sizeof(citnntp)); if (CC->nologin==1) { cprintf("451 Too many connections are already open; please try again later.\r\n"); CC->kill_me = KILLME_MAX_SESSIONS_EXCEEDED; return; } // Display the standard greeting cprintf("200 %s NNTP Citadel server is not finished yet\r\n", CtdlGetConfigStr("c_fqdn")); }
/* * Wrapper function for ldap_initialize() that consistently fills in the correct fields */ int ctdl_ldap_initialize(LDAP **ld) { char server_url[256]; int ret; snprintf(server_url, sizeof server_url, "ldap://%s:%d", CtdlGetConfigStr("c_ldap_host"), CtdlGetConfigInt("c_ldap_port")); ret = ldap_initialize(ld, server_url); if (ret != LDAP_SUCCESS) { syslog(LOG_ALERT, "LDAP: Could not connect to %s : %s", server_url, strerror(errno) ); *ld = NULL; return(errno); } return(ret); }
/* * admin command: kill the current room */ void cmd_kill(char *argbuf) { char deleted_room_name[ROOMNAMELEN]; char msg[SIZ]; int kill_ok; kill_ok = extract_int(argbuf, 0); if (CtdlDoIHavePermissionToDeleteThisRoom(&CC->room) == 0) { cprintf("%d Can't delete this room.\n", ERROR + NOT_HERE); return; } if (kill_ok) { if (CC->room.QRflags & QR_MAILBOX) { safestrncpy(deleted_room_name, &CC->room.QRname[11], sizeof deleted_room_name); } else { safestrncpy(deleted_room_name, CC->room.QRname, sizeof deleted_room_name); } /* Do the dirty work */ CtdlScheduleRoomForDeletion(&CC->room); /* Return to the Lobby */ CtdlUserGoto(CtdlGetConfigStr("c_baseroom"), 0, 0, NULL, NULL, NULL, NULL); /* tell the world what we did */ snprintf(msg, sizeof msg, "The room \"%s\" has been deleted by %s.\n", deleted_room_name, (CC->logged_in ? CC->curr_user : "******") ); CtdlAideMessage(msg, "Room Purger Message"); cprintf("%d '%s' deleted.\n", CIT_OK, deleted_room_name); } else { cprintf("%d ok to delete.\n", CIT_OK); } }
/* * 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"); }
/* * Look up a user in the directory to see if this is an account that can be authenticated */ int CtdlTryUserLDAP(char *username, char *found_dn, int found_dn_size, char *fullname, int fullname_size, uid_t *uid, int lookup_based_on_username) { LDAP *ldserver = NULL; int i; LDAPMessage *search_result = NULL; LDAPMessage *entry = NULL; char searchstring[1024]; struct timeval tv; char **values; char *user_dn = NULL; if (fullname) safestrncpy(fullname, username, fullname_size); if (ctdl_ldap_initialize(&ldserver) != LDAP_SUCCESS) { return(errno); } ldap_set_option(ldserver, LDAP_OPT_PROTOCOL_VERSION, &ctdl_require_ldap_version); ldap_set_option(ldserver, LDAP_OPT_REFERRALS, (void *)LDAP_OPT_OFF); striplt(CtdlGetConfigStr("c_ldap_bind_dn")); striplt(CtdlGetConfigStr("c_ldap_bind_pw")); syslog(LOG_DEBUG, "LDAP bind DN: %s", CtdlGetConfigStr("c_ldap_bind_dn")); i = ldap_simple_bind_s(ldserver, (!IsEmptyStr(CtdlGetConfigStr("c_ldap_bind_dn")) ? CtdlGetConfigStr("c_ldap_bind_dn") : NULL), (!IsEmptyStr(CtdlGetConfigStr("c_ldap_bind_pw")) ? CtdlGetConfigStr("c_ldap_bind_pw") : NULL) ); if (i != LDAP_SUCCESS) { syslog(LOG_ALERT, "LDAP: Cannot bind: %s (%d)", ldap_err2string(i), i); return(i); } tv.tv_sec = 10; tv.tv_usec = 0; if (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_LDAP_AD) { if (lookup_based_on_username != 0) snprintf(searchstring, sizeof(searchstring), "(displayName=%s)",username); else snprintf(searchstring, sizeof(searchstring), "(sAMAccountName=%s)", username); } else { if (lookup_based_on_username != 0) snprintf(searchstring, sizeof(searchstring), "(cn=%s)",username); else snprintf(searchstring, sizeof(searchstring), "(&(objectclass=posixAccount)(uid=%s))", username); } syslog(LOG_DEBUG, "LDAP search: %s", searchstring); (void) ldap_search_ext_s( ldserver, /* ld */ CtdlGetConfigStr("c_ldap_base_dn"), /* base */ LDAP_SCOPE_SUBTREE, /* scope */ searchstring, /* filter */ NULL, /* attrs (all attributes) */ 0, /* attrsonly (attrs + values) */ NULL, /* serverctrls (none) */ NULL, /* clientctrls (none) */ &tv, /* timeout */ 1, /* sizelimit (1 result max) */ &search_result /* res */ ); /* Ignore the return value of ldap_search_ext_s(). Sometimes it returns an error even when * the search succeeds. Instead, we check to see whether search_result is still NULL. */ if (search_result == NULL) { syslog(LOG_DEBUG, "LDAP search: zero results were returned"); ldap_unbind(ldserver); return(2); } /* At this point we've got at least one result from our query. If there are multiple * results, we still only look at the first one. */ entry = ldap_first_entry(ldserver, search_result); if (entry) { user_dn = ldap_get_dn(ldserver, entry); if (user_dn) { syslog(LOG_DEBUG, "dn = %s", user_dn); } if (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_LDAP_AD) { values = ldap_get_values(ldserver, search_result, "displayName"); if (values) { if (values[0]) { if (fullname) safestrncpy(fullname, values[0], fullname_size); syslog(LOG_DEBUG, "displayName = %s", values[0]); } ldap_value_free(values); } } else { values = ldap_get_values(ldserver, search_result, "cn"); if (values) { if (values[0]) { if (fullname) safestrncpy(fullname, values[0], fullname_size); syslog(LOG_DEBUG, "cn = %s", values[0]); } ldap_value_free(values); } } /* If we know the username is the CN/displayName, we already set the uid*/ if (lookup_based_on_username==0) { if (CtdlGetConfigInt("c_auth_mode") == AUTHMODE_LDAP_AD) { values = ldap_get_values(ldserver, search_result, "objectGUID"); if (values) { if (values[0]) { if (uid != NULL) { *uid = abs(HashLittle(values[0], strlen(values[0]))); syslog(LOG_DEBUG, "uid hashed from objectGUID = %d", *uid); } } ldap_value_free(values); } } else { values = ldap_get_values(ldserver, search_result, "uidNumber"); if (values) { if (values[0]) { syslog(LOG_DEBUG, "uidNumber = %s", values[0]); if (uid != NULL) { *uid = atoi(values[0]); } } ldap_value_free(values); } } } } /* free the results */ ldap_msgfree(search_result); /* unbind so we can go back in as the authenticating user */ ldap_unbind(ldserver); if (!user_dn) { syslog(LOG_DEBUG, "No such user was found."); return(4); } if (found_dn) safestrncpy(found_dn, user_dn, found_dn_size); ldap_memfree(user_dn); return(0); }
/* * Fetch a specific revision of a wiki page. The "operation" string may be set to "fetch" in order * to simply fetch the desired revision and store it in a temporary location for viewing, or "revert" * to revert the currently active page to that revision. */ void wiki_rev(char *pagename, char *rev, char *operation) { struct CitContext *CCC = CC; int r; char history_page_name[270]; long msgnum; char temp[PATH_MAX]; struct CtdlMessage *msg; FILE *fp; struct HistoryEraserCallBackData hecbd; long len = 0L; int rv; r = CtdlDoIHavePermissionToReadMessagesInThisRoom(); if (r != om_ok) { if (r == om_not_logged_in) { cprintf("%d Not logged in.\n", ERROR + NOT_LOGGED_IN); } else { cprintf("%d An unknown error has occurred.\n", ERROR); } return; } if (!strcasecmp(operation, "revert")) { r = CtdlDoIHavePermissionToPostInThisRoom(temp, sizeof temp, NULL, POST_LOGGED_IN, 0); if (r != 0) { cprintf("%d %s\n", r, temp); return; } } /* Begin by fetching the current version of the page. We're going to patch * backwards through the diffs until we get the one we want. */ msgnum = CtdlLocateMessageByEuid(pagename, &CCC->room); if (msgnum > 0L) { msg = CtdlFetchMessage(msgnum, 1, 1); } else { msg = NULL; } if ((msg != NULL) && CM_IsEmpty(msg, eMesageText)) { CM_Free(msg); msg = NULL; } if (msg == NULL) { cprintf("%d Page '%s' was not found.\n", ERROR+MESSAGE_NOT_FOUND, pagename); return; } /* Output it to a temporary file */ CtdlMakeTempFileName(temp, sizeof temp); fp = fopen(temp, "w"); if (fp != NULL) { r = fwrite(msg->cm_fields[eMesageText], msg->cm_lengths[eMesageText], 1, fp); fclose(fp); } else { syslog(LOG_ALERT, "Cannot open %s: %s\n", temp, strerror(errno)); } CM_Free(msg); /* Get the revision history */ snprintf(history_page_name, sizeof history_page_name, "%s_HISTORY_", pagename); msgnum = CtdlLocateMessageByEuid(history_page_name, &CCC->room); if (msgnum > 0L) { msg = CtdlFetchMessage(msgnum, 1, 1); } else { msg = NULL; } if ((msg != NULL) && CM_IsEmpty(msg, eMesageText)) { CM_Free(msg); msg = NULL; } if (msg == NULL) { cprintf("%d Revision history for '%s' was not found.\n", ERROR+MESSAGE_NOT_FOUND, pagename); return; } /* Start patching backwards (newest to oldest) through the revision history until we * hit the revision uuid requested by the user. (The callback will perform each one.) */ memset(&hecbd, 0, sizeof(struct HistoryEraserCallBackData)); hecbd.tempfilename = temp; hecbd.stop_when = rev; striplt(hecbd.stop_when); mime_parser(CM_RANGE(msg, eMesageText), *wiki_rev_callback, NULL, NULL, (void *)&hecbd, 0); CM_Free(msg); /* Were we successful? */ if (hecbd.done == 0) { cprintf("%d Revision '%s' of page '%s' was not found.\n", ERROR + MESSAGE_NOT_FOUND, rev, pagename ); } /* We have the desired revision on disk. Now do something with it. */ else if ( (!strcasecmp(operation, "fetch")) || (!strcasecmp(operation, "revert")) ) { msg = malloc(sizeof(struct CtdlMessage)); memset(msg, 0, sizeof(struct CtdlMessage)); msg->cm_magic = CTDLMESSAGE_MAGIC; msg->cm_anon_type = MES_NORMAL; msg->cm_format_type = FMT_RFC822; fp = fopen(temp, "r"); if (fp) { char *msgbuf; fseek(fp, 0L, SEEK_END); len = ftell(fp); fseek(fp, 0L, SEEK_SET); msgbuf = malloc(len + 1); rv = fread(msgbuf, len, 1, fp); syslog(LOG_DEBUG, "did %d blocks of %ld bytes\n", rv, len); msgbuf[len] = '\0'; CM_SetAsField(msg, eMesageText, &msgbuf, len); fclose(fp); } if (len <= 0) { msgnum = (-1L); } else if (!strcasecmp(operation, "fetch")) { CM_SetField(msg, eAuthor, HKEY("Citadel")); CtdlCreateRoom(wwm, 5, "", 0, 1, 1, VIEW_BBS); /* Not an error if already exists */ msgnum = CtdlSubmitMsg(msg, NULL, wwm, 0); /* Store the revision here */ /* * WARNING: VILE SLEAZY HACK * This will avoid the 'message xxx is not in this room' security error, * but only if the client fetches the message we just generated immediately * without first trying to perform other fetch operations. */ if (CCC->cached_msglist != NULL) { free(CCC->cached_msglist); CCC->cached_msglist = NULL; CCC->cached_num_msgs = 0; } CCC->cached_msglist = malloc(sizeof(long)); if (CCC->cached_msglist != NULL) { CCC->cached_num_msgs = 1; CCC->cached_msglist[0] = msgnum; } } else if (!strcasecmp(operation, "revert")) { CM_SetFieldLONG(msg, eTimestamp, time(NULL)); if (!IsEmptyStr(CCC->user.fullname)) { CM_SetField(msg, eAuthor, CCC->user.fullname, strlen(CCC->user.fullname)); } if (!IsEmptyStr(CCC->cs_inet_email)) { CM_SetField(msg, erFc822Addr, CCC->cs_inet_email, strlen(CCC->cs_inet_email)); } if (!IsEmptyStr(CCC->room.QRname)) { CM_SetField(msg, eOriginalRoom, CCC->room.QRname, strlen(CCC->room.QRname)); } CM_SetField(msg, eNodeName, CtdlGetConfigStr("c_nodename"), strlen(CtdlGetConfigStr("c_nodename"))); if (!IsEmptyStr(pagename)) { CM_SetField(msg, eExclusiveID, pagename, strlen(pagename)); } msgnum = CtdlSubmitMsg(msg, NULL, "", 0); /* Replace the current revision */ } else { /* Theoretically it is impossible to get here, but throw an error anyway */ msgnum = (-1L); } CM_Free(msg); if (msgnum >= 0L) { cprintf("%d %ld\n", CIT_OK, msgnum); /* Give the client a msgnum */ } else { cprintf("%d Error %ld has occurred.\n", ERROR+INTERNAL_ERROR, msgnum); } } /* We did all this work for nothing. Express anguish to the caller. */ else { cprintf("%d An unknown operation was requested.\n", ERROR+CMD_NOT_SUPPORTED); } unlink(temp); return; }
/* * Validate recipients, count delivery types and errors, and handle aliasing * FIXME check for dupes!!!!! * * Returns 0 if all addresses are ok, ret->num_error = -1 if no addresses * were specified, or the number of addresses found invalid. * * Caller needs to free the result using free_recipients() */ recptypes *validate_recipients(const char *supplied_recipients, const char *RemoteIdentifier, int Flags) { struct CitContext *CCC = CC; recptypes *ret; char *recipients = NULL; char *org_recp; char this_recp[256]; char this_recp_cooked[256]; char append[SIZ]; long len; int num_recps = 0; int i, j; int mailtype; int invalid; struct ctdluser tempUS; struct ctdlroom tempQR; struct ctdlroom tempQR2; int err = 0; char errmsg[SIZ]; int in_quotes = 0; /* Initialize */ ret = (recptypes *) malloc(sizeof(recptypes)); if (ret == NULL) return(NULL); /* Set all strings to null and numeric values to zero */ memset(ret, 0, sizeof(recptypes)); if (supplied_recipients == NULL) { recipients = strdup(""); } else { recipients = strdup(supplied_recipients); } /* Allocate some memory. Yes, this allocates 500% more memory than we will * actually need, but it's healthier for the heap than doing lots of tiny * realloc() calls instead. */ len = strlen(recipients) + 1024; ret->errormsg = malloc(len); ret->recp_local = malloc(len); ret->recp_internet = malloc(len); ret->recp_ignet = malloc(len); ret->recp_room = malloc(len); ret->display_recp = malloc(len); ret->recp_orgroom = malloc(len); org_recp = malloc(len); ret->errormsg[0] = 0; ret->recp_local[0] = 0; ret->recp_internet[0] = 0; ret->recp_ignet[0] = 0; ret->recp_room[0] = 0; ret->recp_orgroom[0] = 0; ret->display_recp[0] = 0; ret->recptypes_magic = RECPTYPES_MAGIC; /* Change all valid separator characters to commas */ for (i=0; !IsEmptyStr(&recipients[i]); ++i) { if ((recipients[i] == ';') || (recipients[i] == '|')) { recipients[i] = ','; } } /* Now start extracting recipients... */ while (!IsEmptyStr(recipients)) { for (i=0; i<=strlen(recipients); ++i) { if (recipients[i] == '\"') in_quotes = 1 - in_quotes; if ( ( (recipients[i] == ',') && (!in_quotes) ) || (recipients[i] == 0) ) { safestrncpy(this_recp, recipients, i+1); this_recp[i] = 0; if (recipients[i] == ',') { strcpy(recipients, &recipients[i+1]); } else { strcpy(recipients, ""); } break; } } striplt(this_recp); if (IsEmptyStr(this_recp)) break; MSG_syslog(LOG_DEBUG, "Evaluating recipient #%d: %s\n", num_recps, this_recp); ++num_recps; strcpy(org_recp, this_recp); alias(this_recp); alias(this_recp); mailtype = alias(this_recp); for (j = 0; !IsEmptyStr(&this_recp[j]); ++j) { if (this_recp[j]=='_') { this_recp_cooked[j] = ' '; } else { this_recp_cooked[j] = this_recp[j]; } } this_recp_cooked[j] = '\0'; invalid = 0; errmsg[0] = 0; switch(mailtype) { case MES_LOCAL: if (!strcasecmp(this_recp, "sysop")) { ++ret->num_room; strcpy(this_recp, CtdlGetConfigStr("c_aideroom")); if (!IsEmptyStr(ret->recp_room)) { strcat(ret->recp_room, "|"); } strcat(ret->recp_room, this_recp); } else if ( (!strncasecmp(this_recp, "room_", 5)) && (!CtdlGetRoom(&tempQR, &this_recp_cooked[5])) ) { /* Save room so we can restore it later */ tempQR2 = CCC->room; CCC->room = tempQR; /* Check permissions to send mail to this room */ err = CtdlDoIHavePermissionToPostInThisRoom( errmsg, sizeof errmsg, RemoteIdentifier, Flags, 0 /* 0 = not a reply */ ); if (err) { ++ret->num_error; invalid = 1; } else { ++ret->num_room; if (!IsEmptyStr(ret->recp_room)) { strcat(ret->recp_room, "|"); } strcat(ret->recp_room, &this_recp_cooked[5]); if (!IsEmptyStr(ret->recp_orgroom)) { strcat(ret->recp_orgroom, "|"); } strcat(ret->recp_orgroom, org_recp); } /* Restore room in case something needs it */ CCC->room = tempQR2; } else if (CtdlGetUser(&tempUS, this_recp) == 0) { ++ret->num_local; strcpy(this_recp, tempUS.fullname); if (!IsEmptyStr(ret->recp_local)) { strcat(ret->recp_local, "|"); } strcat(ret->recp_local, this_recp); } else if (CtdlGetUser(&tempUS, this_recp_cooked) == 0) { ++ret->num_local; strcpy(this_recp, tempUS.fullname); if (!IsEmptyStr(ret->recp_local)) { strcat(ret->recp_local, "|"); } strcat(ret->recp_local, this_recp); } else { ++ret->num_error; invalid = 1; } break; case MES_INTERNET: /* Yes, you're reading this correctly: if the target * domain points back to the local system or an attached * Citadel directory, the address is invalid. That's * because if the address were valid, we would have * already translated it to a local address by now. */ if (IsDirectory(this_recp, 0)) { ++ret->num_error; invalid = 1; } else { ++ret->num_internet; if (!IsEmptyStr(ret->recp_internet)) { strcat(ret->recp_internet, "|"); } strcat(ret->recp_internet, this_recp); } break; case MES_IGNET: ++ret->num_ignet; if (!IsEmptyStr(ret->recp_ignet)) { strcat(ret->recp_ignet, "|"); } strcat(ret->recp_ignet, this_recp); break; case MES_ERROR: ++ret->num_error; invalid = 1; break; } if (invalid) { if (IsEmptyStr(errmsg)) { snprintf(append, sizeof append, "Invalid recipient: %s", this_recp); } else { snprintf(append, sizeof append, "%s", errmsg); } if ( (strlen(ret->errormsg) + strlen(append) + 3) < SIZ) { if (!IsEmptyStr(ret->errormsg)) { strcat(ret->errormsg, "; "); } strcat(ret->errormsg, append); } } else { if (IsEmptyStr(ret->display_recp)) { strcpy(append, this_recp); } else { snprintf(append, sizeof append, ", %s", this_recp); } if ( (strlen(ret->display_recp)+strlen(append)) < SIZ) { strcat(ret->display_recp, append); } } } free(org_recp); if ((ret->num_local + ret->num_internet + ret->num_ignet + ret->num_room + ret->num_error) == 0) { ret->num_error = (-1); strcpy(ret->errormsg, "No recipients specified."); } MSGM_syslog(LOG_DEBUG, "validate_recipients()\n"); MSG_syslog(LOG_DEBUG, " local: %d <%s>\n", ret->num_local, ret->recp_local); MSG_syslog(LOG_DEBUG, " room: %d <%s>\n", ret->num_room, ret->recp_room); MSG_syslog(LOG_DEBUG, " inet: %d <%s>\n", ret->num_internet, ret->recp_internet); MSG_syslog(LOG_DEBUG, " ignet: %d <%s>\n", ret->num_ignet, ret->recp_ignet); MSG_syslog(LOG_DEBUG, " error: %d <%s>\n", ret->num_error, ret->errormsg); free(recipients); return(ret); }
eNextState EvConnectSock(AsyncIO *IO, double conn_timeout, double first_rw_timeout, int ReadFirst) { struct sockaddr_in egress_sin; int fdflags; int rc = -1; SetEVState(IO, eIOConnectSock); become_session(IO->CitContext); if (ReadFirst) { IO->NextState = eReadMessage; } else { IO->NextState = eSendReply; } IO->SendBuf.fd = IO->RecvBuf.fd = socket( (IO->ConnectMe->IPv6)?PF_INET6:PF_INET, SOCK_STREAM, IPPROTO_TCP); if (IO->SendBuf.fd < 0) { EV_syslog(LOG_ERR, "EVENT: socket() failed: %s\n", strerror(errno)); StrBufPrintf(IO->ErrMsg, "Failed to create socket: %s", strerror(errno)); IO->SendBuf.fd = IO->RecvBuf.fd = 0; return eAbort; } fdflags = fcntl(IO->SendBuf.fd, F_GETFL); if (fdflags < 0) { EV_syslog(LOG_ERR, "EVENT: unable to get socket %d flags! %s \n", IO->SendBuf.fd, strerror(errno)); StrBufPrintf(IO->ErrMsg, "Failed to get socket %d flags: %s", IO->SendBuf.fd, strerror(errno)); close(IO->SendBuf.fd); IO->SendBuf.fd = IO->RecvBuf.fd = 0; return eAbort; } fdflags = fdflags | O_NONBLOCK; if (fcntl(IO->SendBuf.fd, F_SETFL, fdflags) < 0) { EV_syslog( LOG_ERR, "EVENT: unable to set socket %d nonblocking flags! %s \n", IO->SendBuf.fd, strerror(errno)); StrBufPrintf(IO->ErrMsg, "Failed to set socket flags: %s", strerror(errno)); close(IO->SendBuf.fd); IO->SendBuf.fd = IO->RecvBuf.fd = 0; return eAbort; } /* TODO: maye we could use offsetof() to calc the position of data... * http://doc.dvgu.ru/devel/ev.html#associating_custom_data_with_a_watcher */ ev_io_init(&IO->recv_event, IO_recv_callback, IO->RecvBuf.fd, EV_READ); IO->recv_event.data = IO; ev_io_init(&IO->send_event, IO_send_callback, IO->SendBuf.fd, EV_WRITE); IO->send_event.data = IO; ev_timer_init(&IO->conn_fail, IO_connfail_callback, conn_timeout, 0); IO->conn_fail.data = IO; ev_timer_init(&IO->rw_timeout, IO_Timeout_callback, first_rw_timeout,0); IO->rw_timeout.data = IO; /* for debugging you may bypass it like this: * IO->Addr.sin_addr.s_addr = inet_addr("127.0.0.1"); * ((struct sockaddr_in)IO->ConnectMe->Addr).sin_addr.s_addr = * inet_addr("127.0.0.1"); */ if (IO->ConnectMe->IPv6) { rc = connect(IO->SendBuf.fd, &IO->ConnectMe->Addr, sizeof(struct sockaddr_in6)); } else { /* If citserver is bound to a specific IP address on the host, make * sure we use that address for outbound connections. */ memset(&egress_sin, 0, sizeof(egress_sin)); egress_sin.sin_family = AF_INET; if (!IsEmptyStr(CtdlGetConfigStr("c_ip_addr"))) { egress_sin.sin_addr.s_addr = inet_addr(CtdlGetConfigStr("c_ip_addr")); if (egress_sin.sin_addr.s_addr == !INADDR_ANY) { egress_sin.sin_addr.s_addr = INADDR_ANY; } /* If this bind fails, no problem; we can still use INADDR_ANY */ bind(IO->SendBuf.fd, (struct sockaddr *)&egress_sin, sizeof(egress_sin)); } rc = connect(IO->SendBuf.fd, (struct sockaddr_in *)&IO->ConnectMe->Addr, sizeof(struct sockaddr_in)); } if (rc >= 0){ SetEVState(IO, eIOConnNow); EV_syslog(LOG_DEBUG, "connect() = %d immediate success.\n", IO->SendBuf.fd); set_start_callback(event_base, IO, 0); return IO->NextState; } else if (errno == EINPROGRESS) { SetEVState(IO, eIOConnWait); EV_syslog(LOG_DEBUG, "connect() = %d have to wait now.\n", IO->SendBuf.fd); ev_io_init(&IO->conn_event, IO_connestd_callback, IO->SendBuf.fd, EV_READ|EV_WRITE); IO->conn_event.data = IO; ev_io_start(event_base, &IO->conn_event); ev_timer_start(event_base, &IO->conn_fail); return IO->NextState; } else { SetEVState(IO, eIOConnfail); ev_idle_init(&IO->conn_fail_immediate, IO_connfailimmediate_callback); IO->conn_fail_immediate.data = IO; ev_idle_start(event_base, &IO->conn_fail_immediate); EV_syslog(LOG_ERR, "connect() = %d failed: %s\n", IO->SendBuf.fd, strerror(errno)); StrBufPrintf(IO->ErrMsg, "Failed to connect: %s", strerror(errno)); return IO->NextState; } return IO->NextState; }
/* * Learn LDAP attributes and stuff them into the vCard. * Returns nonzero if we changed anything. */ int Ctdl_LDAP_to_vCard(char *ldap_dn, struct vCard *v) { int changed_something = 0; LDAP *ldserver = NULL; int i; struct timeval tv; LDAPMessage *search_result = NULL; LDAPMessage *entry = NULL; char **givenName; char **sn; char **cn; char **initials; char **o; char **street; char **l; char **st; char **postalCode; char **telephoneNumber; char **mobile; char **homePhone; char **facsimileTelephoneNumber; char **mail; char **uid; char **homeDirectory; char **uidNumber; char **loginShell; char **gidNumber; char **c; char **title; char **uuid; char *attrs[] = { "*","+",NULL}; if (!ldap_dn) return(0); if (!v) return(0); if (ctdl_ldap_initialize(&ldserver) != LDAP_SUCCESS) { return(0); } ldap_set_option(ldserver, LDAP_OPT_PROTOCOL_VERSION, &ctdl_require_ldap_version); ldap_set_option(ldserver, LDAP_OPT_REFERRALS, (void *)LDAP_OPT_OFF); striplt(CtdlGetConfigStr("c_ldap_bind_dn")); striplt(CtdlGetConfigStr("c_ldap_bind_pw")); syslog(LOG_DEBUG, "LDAP bind DN: %s", CtdlGetConfigStr("c_ldap_bind_dn")); i = ldap_simple_bind_s(ldserver, (!IsEmptyStr(CtdlGetConfigStr("c_ldap_bind_dn")) ? CtdlGetConfigStr("c_ldap_bind_dn") : NULL), (!IsEmptyStr(CtdlGetConfigStr("c_ldap_bind_pw")) ? CtdlGetConfigStr("c_ldap_bind_pw") : NULL) ); if (i != LDAP_SUCCESS) { syslog(LOG_ALERT, "LDAP: Cannot bind: %s (%d)", ldap_err2string(i), i); return(0); } tv.tv_sec = 10; tv.tv_usec = 0; syslog(LOG_DEBUG, "LDAP search: %s", ldap_dn); (void) ldap_search_ext_s( ldserver, /* ld */ ldap_dn, /* base */ LDAP_SCOPE_SUBTREE, /* scope */ NULL, /* filter */ attrs, /* attrs (all attributes) */ 0, /* attrsonly (attrs + values) */ NULL, /* serverctrls (none) */ NULL, /* clientctrls (none) */ &tv, /* timeout */ 1, /* sizelimit (1 result max) */ &search_result /* res */ ); /* Ignore the return value of ldap_search_ext_s(). Sometimes it returns an error even when * the search succeeds. Instead, we check to see whether search_result is still NULL. */ if (search_result == NULL) { syslog(LOG_DEBUG, "LDAP search: zero results were returned"); ldap_unbind(ldserver); return(0); } /* At this point we've got at least one result from our query. If there are multiple * results, we still only look at the first one. */ entry = ldap_first_entry(ldserver, search_result); if (entry) { syslog(LOG_DEBUG, "LDAP search, got user details for vcard."); givenName=ldap_get_values(ldserver, search_result, "givenName"); sn=ldap_get_values(ldserver, search_result, "sn"); cn=ldap_get_values(ldserver, search_result, "cn"); initials=ldap_get_values(ldserver, search_result, "initials"); title=ldap_get_values(ldserver, search_result, "title"); o=ldap_get_values(ldserver, search_result, "o"); street=ldap_get_values(ldserver, search_result, "street"); l=ldap_get_values(ldserver, search_result, "l"); st=ldap_get_values(ldserver, search_result, "st"); postalCode=ldap_get_values(ldserver, search_result, "postalCode"); telephoneNumber=ldap_get_values(ldserver, search_result, "telephoneNumber"); mobile=ldap_get_values(ldserver, search_result, "mobile"); homePhone=ldap_get_values(ldserver, search_result, "homePhone"); facsimileTelephoneNumber=ldap_get_values(ldserver, search_result, "facsimileTelephoneNumber"); mail=ldap_get_values(ldserver, search_result, "mail"); uid=ldap_get_values(ldserver, search_result, "uid"); homeDirectory=ldap_get_values(ldserver, search_result, "homeDirectory"); uidNumber=ldap_get_values(ldserver, search_result, "uidNumber"); loginShell=ldap_get_values(ldserver, search_result, "loginShell"); gidNumber=ldap_get_values(ldserver, search_result, "gidNumber"); c=ldap_get_values(ldserver, search_result, "c"); uuid=ldap_get_values(ldserver, search_result, "entryUUID"); if (street && l && st && postalCode && c) changed_something |= vcard_set_one_prop_iff_different(v,"adr",";;%s;%s;%s;%s;%s",street[0],l[0],st[0],postalCode[0],c[0]); if (telephoneNumber) changed_something |= vcard_set_one_prop_iff_different(v,"tel;work","%s",telephoneNumber[0]); if (facsimileTelephoneNumber) changed_something |= vcard_set_one_prop_iff_different(v,"tel;fax","%s",facsimileTelephoneNumber[0]); if (mobile) changed_something |= vcard_set_one_prop_iff_different(v,"tel;cell","%s",mobile[0]); if (homePhone) changed_something |= vcard_set_one_prop_iff_different(v,"tel;home","%s",homePhone[0]); if (givenName && sn) { if (initials) { changed_something |= vcard_set_one_prop_iff_different(v,"n","%s;%s;%s",sn[0],givenName[0],initials[0]); } else { changed_something |= vcard_set_one_prop_iff_different(v,"n","%s;%s",sn[0],givenName[0]); } } if (mail) { changed_something |= vcard_set_props_iff_different(v,"email;internet",ldap_count_values(mail),mail); } if (uuid) changed_something |= vcard_set_one_prop_iff_different(v,"X-uuid","%s",uuid[0]); if (o) changed_something |= vcard_set_one_prop_iff_different(v,"org","%s",o[0]); if (cn) changed_something |= vcard_set_one_prop_iff_different(v,"fn","%s",cn[0]); if (title) changed_something |= vcard_set_one_prop_iff_different(v,"title","%s",title[0]); if (givenName) ldap_value_free(givenName); if (initials) ldap_value_free(initials); if (sn) ldap_value_free(sn); if (cn) ldap_value_free(cn); if (o) ldap_value_free(o); if (street) ldap_value_free(street); if (l) ldap_value_free(l); if (st) ldap_value_free(st); if (postalCode) ldap_value_free(postalCode); if (telephoneNumber) ldap_value_free(telephoneNumber); if (mobile) ldap_value_free(mobile); if (homePhone) ldap_value_free(homePhone); if (facsimileTelephoneNumber) ldap_value_free(facsimileTelephoneNumber); if (mail) ldap_value_free(mail); if (uid) ldap_value_free(uid); if (homeDirectory) ldap_value_free(homeDirectory); if (uidNumber) ldap_value_free(uidNumber); if (loginShell) ldap_value_free(loginShell); if (gidNumber) ldap_value_free(gidNumber); if (c) ldap_value_free(c); if (title) ldap_value_free(title); if (uuid) ldap_value_free(uuid); } /* free the results */ ldap_msgfree(search_result); /* unbind so we can go back in as the authenticating user */ ldap_unbind(ldserver); return(changed_something); /* tell the caller whether we made any changes */ }
/* * 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 smtp_do_bounce(char *instr, StrBuf *OMsgTxt) { int i; int lines; int status; char buf[1024]; char key[1024]; char addr[1024]; char dsn[1024]; char bounceto[1024]; StrBuf *boundary; int num_bounces = 0; int bounce_this = 0; time_t submitted = 0L; struct CtdlMessage *bmsg = NULL; int give_up = 0; recptypes *valid; int successful_bounce = 0; static int seq = 0; StrBuf *BounceMB; long omsgid = (-1); syslog(LOG_DEBUG, "smtp_do_bounce() called\n"); strcpy(bounceto, ""); boundary = NewStrBufPlain(HKEY("=_Citadel_Multipart_")); StrBufAppendPrintf(boundary, "%s_%04x%04x", CtdlGetConfigStr("c_fqdn"), getpid(), ++seq); lines = num_tokens(instr, '\n'); /* See if it's time to give up on delivery of this message */ for (i=0; i<lines; ++i) { extract_token(buf, instr, i, '\n', sizeof buf); extract_token(key, buf, 0, '|', sizeof key); extract_token(addr, buf, 1, '|', sizeof addr); if (!strcasecmp(key, "submitted")) { submitted = atol(addr); } } if ( (time(NULL) - submitted) > SMTP_GIVE_UP ) { give_up = 1; } /* Start building our bounce message */ bmsg = (struct CtdlMessage *) malloc(sizeof(struct CtdlMessage)); if (bmsg == NULL) return; memset(bmsg, 0, sizeof(struct CtdlMessage)); BounceMB = NewStrBufPlain(NULL, 1024); bmsg->cm_magic = CTDLMESSAGE_MAGIC; bmsg->cm_anon_type = MES_NORMAL; bmsg->cm_format_type = FMT_RFC822; CM_SetField(bmsg, eAuthor, HKEY("Citadel")); CM_SetField(bmsg, eOriginalRoom, HKEY(MAILROOM)); CM_SetField(bmsg, eNodeName, CtdlGetConfigStr("c_nodename"), strlen(CtdlGetConfigStr("c_nodename"))); CM_SetField(bmsg, eMsgSubject, HKEY("Delivery Status Notification (Failure)")); 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\ndue 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); } /* * Now go through the instructions checking for stuff. */ for (i=0; i<lines; ++i) { long addrlen; long dsnlen; extract_token(buf, instr, i, '\n', sizeof buf); extract_token(key, buf, 0, '|', sizeof key); addrlen = extract_token(addr, buf, 1, '|', sizeof addr); status = extract_int(buf, 2); dsnlen = extract_token(dsn, buf, 3, '|', sizeof dsn); bounce_this = 0; syslog(LOG_DEBUG, "key=<%s> addr=<%s> status=%d dsn=<%s>\n", key, addr, status, dsn); if (!strcasecmp(key, "bounceto")) { strcpy(bounceto, addr); } if (!strcasecmp(key, "msgid")) { omsgid = atol(addr); } if (!strcasecmp(key, "remote")) { if (status == 5) bounce_this = 1; if (give_up) bounce_this = 1; } if (bounce_this) { ++num_bounces; StrBufAppendBufPlain(BounceMB, addr, addrlen, 0); StrBufAppendBufPlain(BounceMB, HKEY(": "), 0); StrBufAppendBufPlain(BounceMB, dsn, dsnlen, 0); StrBufAppendBufPlain(BounceMB, HKEY("\r\n"), 0); remove_token(instr, i, '\n'); --i; --lines; } } /* Attach the original message */ if (omsgid >= 0) { StrBufAppendBufPlain(BounceMB, HKEY("--"), 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); if (OMsgTxt == NULL) { CC->redirect_buffer = NewStrBufPlain(NULL, SIZ); CtdlOutputMsg(omsgid, MT_RFC822, HEADERS_ALL, 0, 1, NULL, 0, NULL, NULL, NULL); StrBufAppendBuf(BounceMB, CC->redirect_buffer, 0); FreeStrBuf(&CC->redirect_buffer); } else { StrBufAppendBuf(BounceMB, OMsgTxt, 0); } } /* Close the multipart MIME scope */ StrBufAppendBufPlain(BounceMB, HKEY("--"), 0); StrBufAppendBuf(BounceMB, boundary, 0); StrBufAppendBufPlain(BounceMB, HKEY("--\r\n"), 0); CM_SetAsFieldSB(bmsg, eMesageText, &BounceMB); /* Deliver the bounce if there's anything worth mentioning */ syslog(LOG_DEBUG, "num_bounces = %d\n", num_bounces); if (num_bounces > 0) { /* First try the user who sent the message */ if (IsEmptyStr(bounceto)) syslog(LOG_ERR, "No bounce address specified\n"); else syslog(LOG_DEBUG, "bounce to user <%s>\n", bounceto); /* Can we deliver the bounce to the original sender? */ valid = validate_recipients(bounceto, smtp_get_Recipients (), 0); if (valid != NULL) { if (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 */ if (valid != NULL) { free_recipients(valid); } } FreeStrBuf(&boundary); CM_Free(bmsg); syslog(LOG_DEBUG, "Done processing bounces\n"); }
/* * Before allowing a wiki page save to execute, we have to perform version control. * This involves fetching the old version of the page if it exists. */ int wiki_upload_beforesave(struct CtdlMessage *msg, recptypes *recp) { struct CitContext *CCC = CC; long old_msgnum = (-1L); struct CtdlMessage *old_msg = NULL; long history_msgnum = (-1L); struct CtdlMessage *history_msg = NULL; char diff_old_filename[PATH_MAX]; char diff_new_filename[PATH_MAX]; char diff_out_filename[PATH_MAX]; char diff_cmd[PATH_MAX]; FILE *fp; int rv; char history_page[1024]; long history_page_len; char boundary[256]; char prefixed_boundary[258]; char buf[1024]; char *diffbuf = NULL; size_t diffbuf_len = 0; char *ptr = NULL; long newmsgid; StrBuf *msgidbuf; if (!CCC->logged_in) return(0); /* Only do this if logged in. */ /* Is this a room with a Wiki in it, don't run this hook. */ if ((CCC->room.QRdefaultview != VIEW_WIKI) && (CCC->room.QRdefaultview != VIEW_WIKIMD)) { return(0); } /* If this isn't a MIME message, don't bother. */ if (msg->cm_format_type != 4) return(0); /* If there's no EUID we can't do this. Reject the post. */ if (CM_IsEmpty(msg, eExclusiveID)) return(1); newmsgid = get_new_message_number(); msgidbuf = NewStrBuf(); StrBufPrintf(msgidbuf, "%08lX-%08lX@%s/%s", (long unsigned int) time(NULL), (long unsigned int) newmsgid, CtdlGetConfigStr("c_fqdn"), msg->cm_fields[eExclusiveID] ); CM_SetAsFieldSB(msg, emessageId, &msgidbuf); history_page_len = snprintf(history_page, sizeof history_page, "%s_HISTORY_", msg->cm_fields[eExclusiveID]); /* Make sure we're saving a real wiki page rather than a wiki history page. * This is important in order to avoid recursing infinitely into this hook. */ if ( (msg->cm_lengths[eExclusiveID] >= 9) && (!strcasecmp(&msg->cm_fields[eExclusiveID][msg->cm_lengths[eExclusiveID]-9], "_HISTORY_")) ) { syslog(LOG_DEBUG, "History page not being historied\n"); return(0); } /* If there's no message text, obviously this is all b0rken and shouldn't happen at all */ if (CM_IsEmpty(msg, eMesageText)) return(0); /* Set the message subject identical to the page name */ CM_CopyField(msg, eMsgSubject, eExclusiveID); /* See if we can retrieve the previous version. */ old_msgnum = CtdlLocateMessageByEuid(msg->cm_fields[eExclusiveID], &CCC->room); if (old_msgnum > 0L) { old_msg = CtdlFetchMessage(old_msgnum, 1, 1); } else { old_msg = NULL; } if ((old_msg != NULL) && (CM_IsEmpty(old_msg, eMesageText))) { /* old version is corrupt? */ CM_Free(old_msg); old_msg = NULL; } /* If no changes were made, don't bother saving it again */ if ((old_msg != NULL) && (!strcmp(msg->cm_fields[eMesageText], old_msg->cm_fields[eMesageText]))) { CM_Free(old_msg); return(1); } /* * Generate diffs */ CtdlMakeTempFileName(diff_old_filename, sizeof diff_old_filename); CtdlMakeTempFileName(diff_new_filename, sizeof diff_new_filename); CtdlMakeTempFileName(diff_out_filename, sizeof diff_out_filename); if (old_msg != NULL) { fp = fopen(diff_old_filename, "w"); rv = fwrite(old_msg->cm_fields[eMesageText], old_msg->cm_lengths[eMesageText], 1, fp); fclose(fp); CM_Free(old_msg); } fp = fopen(diff_new_filename, "w"); rv = fwrite(msg->cm_fields[eMesageText], msg->cm_lengths[eMesageText], 1, fp); fclose(fp); snprintf(diff_cmd, sizeof diff_cmd, DIFF " -u %s %s >%s", diff_new_filename, ((old_msg != NULL) ? diff_old_filename : "/dev/null"), diff_out_filename ); syslog(LOG_DEBUG, "diff cmd: %s", diff_cmd); rv = system(diff_cmd); syslog(LOG_DEBUG, "diff cmd returned %d", rv); diffbuf_len = 0; diffbuf = NULL; fp = fopen(diff_out_filename, "r"); if (fp == NULL) { fp = fopen("/dev/null", "r"); } if (fp != NULL) { fseek(fp, 0L, SEEK_END); diffbuf_len = ftell(fp); fseek(fp, 0L, SEEK_SET); diffbuf = malloc(diffbuf_len + 1); fread(diffbuf, diffbuf_len, 1, fp); diffbuf[diffbuf_len] = '\0'; fclose(fp); } syslog(LOG_DEBUG, "diff length is "SIZE_T_FMT" bytes", diffbuf_len); unlink(diff_old_filename); unlink(diff_new_filename); unlink(diff_out_filename); /* Determine whether this was a bogus (empty) edit */ if ((diffbuf_len = 0) && (diffbuf != NULL)) { free(diffbuf); diffbuf = NULL; } if (diffbuf == NULL) { return(1); /* No changes at all? Abandon the post entirely! */ } /* Now look for the existing edit history */ history_msgnum = CtdlLocateMessageByEuid(history_page, &CCC->room); history_msg = NULL; if (history_msgnum > 0L) { history_msg = CtdlFetchMessage(history_msgnum, 1, 1); } /* Create a new history message if necessary */ if (history_msg == NULL) { char *buf; long len; history_msg = malloc(sizeof(struct CtdlMessage)); memset(history_msg, 0, sizeof(struct CtdlMessage)); history_msg->cm_magic = CTDLMESSAGE_MAGIC; history_msg->cm_anon_type = MES_NORMAL; history_msg->cm_format_type = FMT_RFC822; CM_SetField(history_msg, eAuthor, HKEY("Citadel")); if (!IsEmptyStr(CCC->room.QRname)){ CM_SetField(history_msg, eRecipient, CCC->room.QRname, strlen(CCC->room.QRname)); } CM_SetField(history_msg, eExclusiveID, history_page, history_page_len); CM_SetField(history_msg, eMsgSubject, history_page, history_page_len); CM_SetField(history_msg, eSuppressIdx, HKEY("1")); /* suppress full text indexing */ snprintf(boundary, sizeof boundary, "Citadel--Multipart--%04x--%08lx", getpid(), time(NULL)); buf = (char*) malloc(1024); len = snprintf(buf, 1024, "Content-type: multipart/mixed; boundary=\"%s\"\n\n" "This is a Citadel wiki history encoded as multipart MIME.\n" "Each part is comprised of a diff script representing one change set.\n" "\n" "--%s--\n", boundary, boundary ); CM_SetAsField(history_msg, eMesageText, &buf, len); } /* Update the history message (regardless of whether it's new or existing) */ /* Remove the Message-ID from the old version of the history message. This will cause a brand * new one to be generated, avoiding an uninitentional hit of the loop zapper when we replicate. */ CM_FlushField(history_msg, emessageId); /* Figure out the boundary string. We do this even when we generated the * boundary string in the above code, just to be safe and consistent. */ *boundary = '\0'; ptr = history_msg->cm_fields[eMesageText]; do { ptr = memreadline(ptr, buf, sizeof buf); if (*ptr != 0) { striplt(buf); if (!IsEmptyStr(buf) && (!strncasecmp(buf, "Content-type:", 13))) { if ( (bmstrcasestr(buf, "multipart") != NULL) && (bmstrcasestr(buf, "boundary=") != NULL) ) { safestrncpy(boundary, bmstrcasestr(buf, "\""), sizeof boundary); char *qu; qu = strchr(boundary, '\"'); if (qu) { strcpy(boundary, ++qu); } qu = strchr(boundary, '\"'); if (qu) { *qu = 0; } } } } } while ( (IsEmptyStr(boundary)) && (*ptr != 0) ); /* * Now look for the first boundary. That is where we need to insert our fun. */ if (!IsEmptyStr(boundary)) { char *MsgText; long MsgTextLen; time_t Now = time(NULL); snprintf(prefixed_boundary, sizeof(prefixed_boundary), "--%s", boundary); CM_GetAsField(history_msg, eMesageText, &MsgText, &MsgTextLen); ptr = bmstrcasestr(MsgText, prefixed_boundary); if (ptr != NULL) { StrBuf *NewMsgText; char uuid[64]; char memo[512]; long memolen; char encoded_memo[1024]; NewMsgText = NewStrBufPlain(NULL, MsgTextLen + diffbuf_len + 1024); generate_uuid(uuid); memolen = snprintf(memo, sizeof(memo), "%s|%ld|%s|%s", uuid, Now, CCC->user.fullname, CtdlGetConfigStr("c_nodename")); memolen = CtdlEncodeBase64(encoded_memo, memo, memolen, 0); StrBufAppendBufPlain(NewMsgText, HKEY("--"), 0); StrBufAppendBufPlain(NewMsgText, boundary, -1, 0); StrBufAppendBufPlain( NewMsgText, HKEY("\n" "Content-type: text/plain\n" "Content-Disposition: inline; filename=\""), 0); StrBufAppendBufPlain(NewMsgText, encoded_memo, memolen, 0); StrBufAppendBufPlain( NewMsgText, HKEY("\"\n" "Content-Transfer-Encoding: 8bit\n" "\n"), 0); StrBufAppendBufPlain(NewMsgText, diffbuf, diffbuf_len, 0); StrBufAppendBufPlain(NewMsgText, HKEY("\n"), 0); StrBufAppendBufPlain(NewMsgText, ptr, MsgTextLen - (ptr - MsgText), 0); free(MsgText); CM_SetAsFieldSB(history_msg, eMesageText, &NewMsgText); } else { CM_SetAsField(history_msg, eMesageText, &MsgText, MsgTextLen); } CM_SetFieldLONG(history_msg, eTimestamp, Now); CtdlSubmitMsg(history_msg, NULL, "", 0); } else { syslog(LOG_ALERT, "Empty boundary string in history message. No history!\n"); } free(diffbuf); CM_Free(history_msg); return(0); }
/* * RDIR command for room directory */ void cmd_rdir(char *cmdbuf) { char buf[256]; char comment[256]; FILE *fd; struct stat statbuf; DIR *filedir = NULL; struct dirent *filedir_entry; int d_namelen; char buf2[SIZ]; char mimebuf[64]; long len; if (CtdlAccessCheck(ac_logged_in)) return; CtdlGetRoom(&CC->room, CC->room.QRname); CtdlGetUser(&CC->user, CC->curr_user); if ((CC->room.QRflags & QR_DIRECTORY) == 0) { cprintf("%d not here.\n", ERROR + NOT_HERE); return; } if (((CC->room.QRflags & QR_VISDIR) == 0) && (CC->user.axlevel < AxAideU) && (CC->user.usernum != CC->room.QRroomaide)) { cprintf("%d not here.\n", ERROR + HIGHER_ACCESS_REQUIRED); return; } snprintf(buf, sizeof buf, "%s/%s", ctdl_file_dir, CC->room.QRdirname); filedir = opendir (buf); if (filedir == NULL) { cprintf("%d not here.\n", ERROR + HIGHER_ACCESS_REQUIRED); return; } cprintf("%d %s|%s/%s\n", LISTING_FOLLOWS, CtdlGetConfigStr("c_fqdn"), ctdl_file_dir, CC->room.QRdirname); snprintf(buf, sizeof buf, "%s/%s/filedir", ctdl_file_dir, CC->room.QRdirname); fd = fopen(buf, "r"); if (fd == NULL) fd = fopen("/dev/null", "r"); while ((filedir_entry = readdir(filedir))) { if (strcasecmp(filedir_entry->d_name, "filedir") && filedir_entry->d_name[0] != '.') { #ifdef _DIRENT_HAVE_D_NAMELEN d_namelen = filedir_entry->d_namlen; #else d_namelen = strlen(filedir_entry->d_name); #endif snprintf(buf, sizeof buf, "%s/%s/%s", ctdl_file_dir, CC->room.QRdirname, filedir_entry->d_name); stat(buf, &statbuf); /* stat the file */ if (!(statbuf.st_mode & S_IFREG)) { snprintf(buf2, sizeof buf2, "\"%s\" appears in the file directory for room \"%s\" but is not a regular file. Directories, named pipes, sockets, etc. are not usable in Citadel room directories.\n", buf, CC->room.QRname ); CtdlAideMessage(buf2, "Unusable data found in room directory"); continue; /* not a useable file type so don't show it */ } safestrncpy(comment, "", sizeof comment); fseek(fd, 0L, 0); /* rewind descriptions file */ /* Get the description from the descriptions file */ while ((fgets(buf, sizeof buf, fd) != NULL) && (IsEmptyStr(comment))) { buf[strlen(buf) - 1] = 0; if ((!strncasecmp(buf, filedir_entry->d_name, d_namelen)) && (buf[d_namelen] == ' ')) safestrncpy(comment, &buf[d_namelen + 1], sizeof comment); } len = extract_token (mimebuf, comment, 0,' ', 64); if ((len <0) || strchr(mimebuf, '/') == NULL) { snprintf (mimebuf, 64, "application/octetstream"); len = 0; } cprintf("%s|%ld|%s|%s\n", filedir_entry->d_name, (long)statbuf.st_size, mimebuf, &comment[len]); } } fclose(fd); closedir(filedir); cprintf("000\n"); }
/* * set room parameters (admin or room admin command) */ void cmd_setr(char *args) { char buf[256]; int new_order = 0; int r; int new_floor; char new_name[ROOMNAMELEN]; if (CtdlAccessCheck(ac_logged_in)) return; if (num_parms(args) >= 6) { new_floor = extract_int(args, 5); } else { new_floor = (-1); /* don't change the floor */ } /* When is a new name more than just a new name? When the old name * has a namespace prefix. */ if (CC->room.QRflags & QR_MAILBOX) { sprintf(new_name, "%010ld.", atol(CC->room.QRname) ); } else { safestrncpy(new_name, "", sizeof new_name); } extract_token(&new_name[strlen(new_name)], args, 0, '|', (sizeof new_name - strlen(new_name))); r = CtdlRenameRoom(CC->room.QRname, new_name, new_floor); if (r == crr_room_not_found) { cprintf("%d Internal error - room not found?\n", ERROR + INTERNAL_ERROR); } else if (r == crr_already_exists) { cprintf("%d '%s' already exists.\n", ERROR + ALREADY_EXISTS, new_name); } else if (r == crr_noneditable) { cprintf("%d Cannot edit this room.\n", ERROR + NOT_HERE); } else if (r == crr_invalid_floor) { cprintf("%d Target floor does not exist.\n", ERROR + INVALID_FLOOR_OPERATION); } else if (r == crr_access_denied) { cprintf("%d You do not have permission to edit '%s'\n", ERROR + HIGHER_ACCESS_REQUIRED, CC->room.QRname); } else if (r != crr_ok) { cprintf("%d Error: CtdlRenameRoom() returned %d\n", ERROR + INTERNAL_ERROR, r); } if (r != crr_ok) { return; } CtdlGetRoom(&CC->room, new_name); /* Now we have to do a bunch of other stuff */ if (num_parms(args) >= 7) { new_order = extract_int(args, 6); if (new_order < 1) new_order = 1; if (new_order > 127) new_order = 127; } CtdlGetRoomLock(&CC->room, CC->room.QRname); /* Directory room */ extract_token(buf, args, 2, '|', sizeof buf); buf[15] = 0; safestrncpy(CC->room.QRdirname, buf, sizeof CC->room.QRdirname); /* Default view */ if (num_parms(args) >= 8) { CC->room.QRdefaultview = extract_int(args, 7); } /* Second set of flags */ if (num_parms(args) >= 9) { CC->room.QRflags2 = extract_int(args, 8); } /* Misc. flags */ CC->room.QRflags = (extract_int(args, 3) | QR_INUSE); /* Clean up a client boo-boo: if the client set the room to * guess-name or passworded, ensure that the private flag is * also set. */ if ((CC->room.QRflags & QR_GUESSNAME) || (CC->room.QRflags & QR_PASSWORDED)) CC->room.QRflags |= QR_PRIVATE; /* Some changes can't apply to BASEROOM */ if (!strncasecmp(CC->room.QRname, CtdlGetConfigStr("c_baseroom"), ROOMNAMELEN)) { CC->room.QRorder = 0; CC->room.QRpasswd[0] = '\0'; CC->room.QRflags &= ~(QR_PRIVATE & QR_PASSWORDED & QR_GUESSNAME & QR_PREFONLY & QR_MAILBOX); CC->room.QRflags |= QR_PERMANENT; } else { /* March order (doesn't apply to AIDEROOM) */ if (num_parms(args) >= 7) CC->room.QRorder = (char) new_order; /* Room password */ extract_token(buf, args, 1, '|', sizeof buf); buf[10] = 0; safestrncpy(CC->room.QRpasswd, buf, sizeof CC->room.QRpasswd); /* Kick everyone out if the client requested it * (by changing the room's generation number) */ if (extract_int(args, 4)) { time(&CC->room.QRgen); } } /* Some changes can't apply to AIDEROOM */ if (!strncasecmp(CC->room.QRname, CtdlGetConfigStr("c_baseroom"), ROOMNAMELEN)) { CC->room.QRorder = 0; CC->room.QRflags &= ~QR_MAILBOX; CC->room.QRflags |= QR_PERMANENT; } /* Write the room record back to disk */ CtdlPutRoomLock(&CC->room); /* Create a room directory if necessary */ if (CC->room.QRflags & QR_DIRECTORY) { snprintf(buf, sizeof buf,"%s/%s", ctdl_file_dir, CC->room.QRdirname); mkdir(buf, 0755); } snprintf(buf, sizeof buf, "The room \"%s\" has been edited by %s.\n", CC->room.QRname, (CC->logged_in ? CC->curr_user : "******") ); CtdlAideMessage(buf, "Room modification Message"); cprintf("%d Ok\n", CIT_OK); }
/* * Split an RFC822-style address into userid, host, and full name * */ void process_rfc822_addr(const char *rfc822, char *user, char *node, char *name) { int a; strcpy(user, ""); strcpy(node, CtdlGetConfigStr("c_fqdn")); strcpy(name, ""); if (rfc822 == NULL) return; /* extract full name - first, it's From minus <userid> */ strcpy(name, rfc822); stripout(name, '<', '>'); /* strip anything to the left of a bang */ while ((!IsEmptyStr(name)) && (haschar(name, '!') > 0)) strcpy(name, &name[1]); /* and anything to the right of a @ or % */ for (a = 0; name[a] != '\0'; ++a) { if (name[a] == '@') { name[a] = 0; break; } if (name[a] == '%') { name[a] = 0; break; } } /* but if there are parentheses, that changes the rules... */ if ((haschar(rfc822, '(') == 1) && (haschar(rfc822, ')') == 1)) { strcpy(name, rfc822); stripallbut(name, '(', ')'); } /* but if there are a set of quotes, that supersedes everything */ if (haschar(rfc822, 34) == 2) { strcpy(name, rfc822); while ((!IsEmptyStr(name)) && (name[0] != 34)) { strcpy(&name[0], &name[1]); } strcpy(&name[0], &name[1]); for (a = 0; name[a] != '\0'; ++a) if (name[a] == 34) { name[a] = 0; break; } } /* extract user id */ strcpy(user, rfc822); /* first get rid of anything in parens */ stripout(user, '(', ')'); /* if there's a set of angle brackets, strip it down to that */ if ((haschar(user, '<') == 1) && (haschar(user, '>') == 1)) { stripallbut(user, '<', '>'); } /* strip anything to the left of a bang */ while ((!IsEmptyStr(user)) && (haschar(user, '!') > 0)) strcpy(user, &user[1]); /* and anything to the right of a @ or % */ for (a = 0; user[a] != '\0'; ++a) { if (user[a] == '@') { user[a] = 0; break; } if (user[a] == '%') { user[a] = 0; break; } } /* extract node name */ strcpy(node, rfc822); /* first get rid of anything in parens */ stripout(node, '(', ')'); /* if there's a set of angle brackets, strip it down to that */ if ((haschar(node, '<') == 1) && (haschar(node, '>') == 1)) { stripallbut(node, '<', '>'); } /* If no node specified, tack ours on instead */ if ( (haschar(node, '@')==0) && (haschar(node, '%')==0) && (haschar(node, '!')==0) ) { strcpy(node, CtdlGetConfigStr("c_nodename")); } else { /* strip anything to the left of a @ */ while ((!IsEmptyStr(node)) && (haschar(node, '@') > 0)) strcpy(node, &node[1]); /* strip anything to the left of a % */ while ((!IsEmptyStr(node)) && (haschar(node, '%') > 0)) strcpy(node, &node[1]); /* reduce multiple system bang paths to node!user */ while ((!IsEmptyStr(node)) && (haschar(node, '!') > 1)) strcpy(node, &node[1]); /* now get rid of the user portion of a node!user string */ for (a = 0; node[a] != '\0'; ++a) if (node[a] == '!') { node[a] = 0; break; } } /* strip leading and trailing spaces in all strings */ striplt(user); striplt(node); striplt(name); /* If we processed a string that had the address in angle brackets * but no name outside the brackets, we now have an empty name. In * this case, use the user portion of the address as the name. */ if ((IsEmptyStr(name)) && (!IsEmptyStr(user))) { strcpy(name, user); } }