--- /dev/null
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <time.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <string.h>
+
+#include "glite/lb/lb_maildir.h"
+
+#define DEFAULT_ROOT "/tmp/lb_maildir"
+
+enum {
+ LBMD_DIR_TMP = 0,
+ LBMD_DIR_NEW,
+ LBMD_DIR_WORK,
+ LBMD_DIR_POST,
+ LBMD_DIR_UNDELIVERABLE
+};
+
+static const char *dirs[] = { "tmp", "new", "work", "post", "undeliverable" };
+
+
+#define MAX_ERR_LEN 1024
+char lbm_errdesc[MAX_ERR_LEN];
+
+
+static int check_mkdir(const char *dir)
+{
+ struct stat sbuf;
+
+ if ( stat(dir, &sbuf) ) {
+ if ( errno == ENOENT ) {
+ if ( mkdir(dir, S_IRWXU) ) return 1;
+ if ( stat(dir, &sbuf) ) return 1;
+ }
+ else return 1;
+ }
+
+ if (!S_ISDIR(sbuf.st_mode)) return 1;
+
+ if (access(dir, R_OK | W_OK)) return 1;
+
+ return 0;
+}
+
+
+int glite_lbu_MaildirInit(
+ const char *dir)
+{
+ const char *root = dir? : DEFAULT_ROOT;
+ char dirname[PATH_MAX];
+ int i;
+
+ lbm_errdesc[0] = '\0';
+ if ( check_mkdir(root) ) {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "%s: %s\n", root, strerror(errno));
+ return 1;
+ }
+
+ for ( i = 0; i < sizeof(dirs)/sizeof((dirs)[0]); i++ ) {
+ snprintf(dirname, PATH_MAX, "%s/%s", root, dirs[i]);
+ if ( check_mkdir(dirname) ) {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "%s: %s\n", dirname, strerror(errno));
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+
+int glite_lbu_MaildirStoreMsg(
+ const char *root,
+ const char *srvname,
+ const char *msg)
+{
+ char fname[PATH_MAX],
+ newfname[PATH_MAX];
+ int fhnd,
+ written,
+ msgsz,
+ ct, i;
+ struct timeval tv;
+
+
+ lbm_errdesc[0] = '\0';
+ if ( !root ) root = DEFAULT_ROOT;
+
+ errno = 0;
+ i = 0;
+ while ( 1 ) {
+ if ( ++i > 10 ) {
+ errno = ECANCELED;
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Maximum tries limit reached with unsuccessful file creation");
+ return -1;
+ }
+ gettimeofday(&tv, NULL);
+ snprintf(fname, PATH_MAX, "%s/%s/%ld_%ld.%s", root, dirs[LBMD_DIR_TMP], tv.tv_sec, tv.tv_usec, srvname);
+ if ( (fhnd = open(fname, O_CREAT|O_EXCL|O_WRONLY, 00600)) < 0 ) {
+ if ( errno == EEXIST ) { usleep(1000); continue; }
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't create file %s", fname);
+ return -1;
+ }
+ break;
+ }
+
+ msgsz = strlen(msg);
+ written = 0;
+ while ( written < msgsz ) {
+ if ( (ct = write(fhnd, msg+written, msgsz-written)) < 0 ) {
+ if ( errno == EINTR ) { errno = 0; continue; }
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't write into file %s", fname);
+ return -1;
+ }
+ written += msgsz;
+ }
+ if ( fsync(fhnd) ) {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't fsync file %s", fname);
+ return -1;
+ }
+ if ( close(fhnd) ) {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't close file %s", fname);
+ return -1;
+ }
+ snprintf(newfname, PATH_MAX, "%s/%s/%s", root, dirs[LBMD_DIR_NEW], strrchr(fname, '/')+1);
+ if ( link(fname, newfname) ) {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't link new file %s", newfname);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int glite_lbu_MaildirTransEnd(
+ const char *root,
+ char *fname,
+ int tstate)
+{
+ char workfname[PATH_MAX],
+ newfname[PATH_MAX],
+ origfname[PATH_MAX];
+ struct stat st;
+
+
+ lbm_errdesc[0] = '\0';
+ if ( !root ) root = DEFAULT_ROOT;
+
+ snprintf(workfname, PATH_MAX, "%s/%s/%s", root, dirs[LBMD_DIR_WORK], fname);
+ unlink(workfname);
+
+ snprintf(origfname, PATH_MAX, "%s/%s/%s", root, dirs[LBMD_DIR_TMP], fname);
+ if ( tstate == LBMD_TRANS_OK ) {
+ unlink(origfname);
+ return 0;
+ }
+
+ if ( tstate == LBMD_TRANS_FAILED ) return 0;
+
+ if ( stat(origfname, &st) ) {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't stat file '%s'", origfname);
+ return -1;
+ }
+
+ snprintf(newfname, PATH_MAX, "%s/%s/%s", root, dirs[LBMD_DIR_POST], fname);
+ if ( link(origfname, newfname) ) {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't link new file %s", newfname);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+int glite_lbu_MaildirRetryTransStart(
+ const char *root,
+ time_t retry,
+ time_t remove,
+ char **msg,
+ char **fname)
+{
+ static DIR *dir = NULL;
+ struct dirent *ent;
+ time_t tlimit_retry, tlimit_remove;
+ struct stat st;
+ char newfname[PATH_MAX],
+ oldfname[PATH_MAX],
+ *buf = NULL;
+ int fhnd,
+ toread, ct,
+ bufsz, bufuse;
+
+
+ lbm_errdesc[0] = '\0';
+ if ( !root ) root = DEFAULT_ROOT;
+
+ if ( !dir ) {
+ char dirname[PATH_MAX];
+ snprintf(dirname, PATH_MAX, "%s/%s", root, dirs[LBMD_DIR_POST]);
+ if ( !(dir = opendir(dirname)) ) {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't open directory '%s'", root);
+ goto err;
+ }
+ }
+
+ tlimit_retry = time(NULL) - retry;
+ tlimit_remove = time(NULL) - remove;
+ do {
+ errno = 0;
+ if ( !(ent = readdir(dir)) ) {
+ if ( errno == EBADF ) {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't read directory '%s'", root);
+ dir = NULL;
+ goto err;
+ } else {
+ closedir(dir);
+ dir = NULL;
+ return 0;
+ }
+ }
+ if ( ent->d_name[0] == '.' ) continue;
+
+ snprintf(oldfname, PATH_MAX, "%s/%s/%s", root, dirs[LBMD_DIR_POST], ent->d_name);
+ snprintf(newfname, PATH_MAX, "%s/%s/%s", root, dirs[LBMD_DIR_WORK], ent->d_name);
+
+ if ( stat(oldfname, &st) < 0 ) {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't stat file '%s'", oldfname);
+ goto err;
+ }
+
+ /* if we cannot deliver the file for 'remove' time limit, */
+ /* it is moved to undeliverable folder and forgotten */
+ if ( st.st_mtime < tlimit_remove ) {
+ snprintf(newfname, PATH_MAX, "%s/%s/%s",
+ root, dirs[LBMD_DIR_UNDELIVERABLE], ent->d_name);
+ }
+ /* try to deliver file every 'retry' seconds */
+ else if ( st.st_ctime > tlimit_retry ) continue;
+
+
+ if ( rename(oldfname, newfname) ) {
+ if ( errno == ENOENT ) {
+ /* maybe some other instance moved this file away... */
+ continue;
+ } else {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't move file '%s'", oldfname);
+ goto err;
+ }
+ } else {
+ if (st.st_mtime < tlimit_remove) {
+ /* we have moved undeliverable file to undeliverable folder */
+ /* no other action needed */
+ snprintf(oldfname, PATH_MAX, "%s/%s/%s", root, dirs[LBMD_DIR_TMP], ent->d_name);
+ unlink(oldfname);
+ continue;
+ } else {
+ /* we have found and moved the file to work folder */
+ /* going to process it */
+ break;
+ }
+ }
+ } while ( 1 );
+
+ if ( (fhnd = open(newfname, O_RDONLY)) < 0 ) {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't open file '%s'", newfname);
+ goto err;
+ }
+
+ bufuse = bufsz = toread = ct = 0;
+ do {
+ errno = 0;
+ if ( bufuse == bufsz ) {
+ char *tmp = realloc(buf, bufsz+BUFSIZ);
+ if ( !tmp ) goto err;
+ buf = tmp;
+ bufsz += BUFSIZ;
+ }
+ toread = bufsz - bufuse;
+ if ( (ct = read(fhnd, buf+bufuse, toread)) < 0 ) {
+ if ( errno == EINTR ) continue;
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't read file '%s'", newfname);
+ goto err;
+ }
+ if ( ct == 0 ) break;
+ bufuse += ct;
+ } while ( ct == toread );
+ close(fhnd);
+
+ if ( !(*fname = strdup(ent->d_name)) ) goto err;
+ buf[bufuse] = 0;
+ *msg = buf;
+ return 1;
+
+
+err:
+ if ( buf ) free(buf);
+
+ return -1;
+}
+
+
+int glite_lbu_MaildirTransStart(
+ const char *root,
+ char **msg,
+ char **fname)
+{
+ static DIR *dir = NULL;
+ struct dirent *ent;
+ char newfname[PATH_MAX],
+ oldfname[PATH_MAX],
+ *buf = NULL;
+ int fhnd,
+ toread, ct,
+ bufsz, bufuse;
+
+
+ lbm_errdesc[0] = '\0';
+ if ( !root ) root = DEFAULT_ROOT;
+
+ if ( !dir ) {
+ char dirname[PATH_MAX];
+ snprintf(dirname, PATH_MAX, "%s/%s", root, dirs[LBMD_DIR_NEW]);
+ if ( !(dir = opendir(dirname)) ) {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't open directory '%s'", root);
+ goto err;
+ }
+ }
+
+ do {
+ errno = 0;
+ if ( !(ent = readdir(dir)) ) {
+ if ( errno == EBADF ) {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't read directory '%s'", root);
+ dir = NULL;
+ goto err;
+ } else {
+ closedir(dir);
+ dir = NULL;
+ return 0;
+ }
+ }
+ if ( ent->d_name[0] == '.' ) continue;
+ snprintf(newfname, PATH_MAX, "%s/%s/%s", root, dirs[LBMD_DIR_WORK], ent->d_name);
+ snprintf(oldfname, PATH_MAX, "%s/%s/%s", root, dirs[LBMD_DIR_NEW], ent->d_name);
+ if ( rename(oldfname, newfname) ) {
+ if ( errno == ENOENT ) {
+ /* maybe some other instance moved this file away... */
+ continue;
+ } else {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't move file '%s'", oldfname);
+ goto err;
+ }
+ } else {
+ /* we have found and moved the file with which we will work now */
+ break;
+ }
+ } while ( 1 );
+
+ if ( (fhnd = open(newfname, O_RDONLY)) < 0 ) {
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't open file '%s'", newfname);
+ goto err;
+ }
+
+ bufuse = bufsz = toread = ct = 0;
+ do {
+ errno = 0;
+ if ( bufuse == bufsz ) {
+ char *tmp = realloc(buf, bufsz+BUFSIZ);
+ if ( !tmp ) goto err;
+ buf = tmp;
+ bufsz += BUFSIZ;
+ }
+ toread = bufsz - bufuse;
+ if ( (ct = read(fhnd, buf+bufuse, toread)) < 0 ) {
+ if ( errno == EINTR ) continue;
+ snprintf(lbm_errdesc, MAX_ERR_LEN, "Can't read file '%s'", newfname);
+ goto err;
+ }
+ if ( ct == 0 ) break;
+ bufuse += ct;
+ } while ( ct == toread );
+ close(fhnd);
+
+ if ( !(*fname = strdup(ent->d_name)) ) goto err;
+ buf[bufuse] = 0;
+ *msg = buf;
+ return 1;
+
+
+err:
+ if ( buf ) free(buf);
+
+ return -1;
+}