bcache.c 6.34 KB
Newer Older
1
/*
2
 * Copyright (C) 2006-2007,2009,2017 Brendan Cully <brendan@kublai.com>
3
 * Copyright (C) 2006,2009 Rocco Rutte <pdmef@gmx.net>
4
 *
5 6 7 8
 *     This program is free software; you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation; either version 2 of the License, or
 *     (at your option) any later version.
9
 *
10 11 12 13
 *     This program is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
14
 *
15 16 17
 *     You should have received a copy of the GNU General Public License
 *     along with this program; if not, write to the Free Software
 *     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
18 19
 */

20
#if HAVE_CONFIG_H
21
#include "config.h"
22 23 24
#endif				/* HAVE_CONFIG_H */

#include <sys/types.h>
25
#include <errno.h>
26
#include <dirent.h>
27
#include <stdio.h>
28 29

#include "mutt.h"
30 31
#include "account.h"
#include "url.h"
32
#include "bcache.h"
33

34
#include "lib.h"
35

36
struct body_cache {
37 38 39 40
  char path[_POSIX_PATH_MAX];
  size_t pathlen;
};

41 42
static int bcache_path(ACCOUNT *account, const char *mailbox,
		       char *dst, size_t dstlen)
43 44
{
  char host[STRING];
45
  char path[_POSIX_PATH_MAX];
46
  ciss_url_t url;
47
  int len;
48 49 50 51

  if (!account || !MessageCachedir || !*MessageCachedir || !dst || !dstlen)
    return -1;

52 53 54
  /* make up a ciss_url_t we can turn into a string */
  memset (&url, 0, sizeof (ciss_url_t));
  mutt_account_tourl (account, &url);
55 56 57 58 59
  /*
   * mutt_account_tourl() just sets up some pointers;
   * if this ever changes, we have a memleak here
   */
  url.path = NULL;
60
  if (url_ciss_tostring (&url, host, sizeof (host), U_PATH) < 0)
61
  {
62
    dprint (1, (debugfile, "bcache_path: URL to string failed\n"));
63 64 65
    return -1;
  }

66
  mutt_encode_path (path, sizeof (path), NONULL (mailbox));
67

68 69 70
  len = snprintf (dst, dstlen-1, "%s/%s%s%s", MessageCachedir,
		  host, path,
		  (*path && path[mutt_strlen (path) - 1] == '/') ? "" : "/");
71

72
  dprint (3, (debugfile, "bcache_path: rc: %d, path: '%s'\n", len, dst));
73

74
  if (len < 0 || (size_t)len >= dstlen-1)
75 76
    return -1;

77
  dprint (3, (debugfile, "bcache_path: directory: '%s'\n", dst));
78 79 80 81

  return 0;
}

82
body_cache_t *mutt_bcache_open (ACCOUNT *account, const char *mailbox)
83
{
84
  struct body_cache *bcache = NULL;
85 86 87 88

  if (!account)
    goto bail;

89 90 91
  bcache = safe_calloc (1, sizeof (struct body_cache));
  if (bcache_path (account, mailbox, bcache->path,
		   sizeof (bcache->path)) < 0)
92
    goto bail;
93
  bcache->pathlen = mutt_strlen (bcache->path);
94 95 96 97 98 99 100 101 102

  return bcache;

bail:
  if (bcache)
    FREE(&bcache);
  return NULL;
}

103
void mutt_bcache_close (body_cache_t **bcache)
104 105 106
{
  if (!bcache || !*bcache)
    return;
107
  FREE(bcache);			/* __FREE_CHECKED__ */
108 109
}

110
FILE* mutt_bcache_get(body_cache_t *bcache, const char *id)
111 112
{
  char path[_POSIX_PATH_MAX];
113
  FILE* fp = NULL;
114 115 116 117 118

  if (!id || !*id || !bcache)
    return NULL;

  path[0] = '\0';
119 120
  safe_strncat (path, sizeof (path), bcache->path, bcache->pathlen);
  safe_strncat (path, sizeof (path), id, mutt_strlen (id));
121

122
  fp = safe_fopen (path, "r");
123

124
  dprint (3, (debugfile, "bcache: get: '%s': %s\n", path, fp == NULL ? "no" : "yes"));
125 126 127 128

  return fp;
}

129
FILE* mutt_bcache_put(body_cache_t *bcache, const char *id, int tmp)
130 131
{
  char path[_POSIX_PATH_MAX];
132 133
  FILE* fp;
  char* s;
134 135 136 137 138
  struct stat sb;

  if (!id || !*id || !bcache)
    return NULL;

139 140 141 142 143 144 145 146 147 148 149 150
  snprintf (path, sizeof (path), "%s%s%s", bcache->path, id,
            tmp ? ".tmp" : "");

  if ((fp = safe_fopen (path, "w+")))
    goto out;

  if (errno == EEXIST)
    /* clean up leftover tmp file */
    mutt_unlink (path);

  s = strchr (path + 1, '/');
  while (!(fp = safe_fopen (path, "w+")) && errno == ENOENT && s)
151
  {
152 153 154
    /* create missing path components */
    *s = '\0';
    if (stat (path, &sb) < 0 && (errno != ENOENT || mkdir (path, 0777) < 0))
155
      return NULL;
156 157
    *s = '/';
    s = strchr (s + 1, '/');
158 159
  }

160 161
  out:
  dprint (3, (debugfile, "bcache: put: '%s'\n", path));
162

163
  return fp;
164 165
}

166
int mutt_bcache_commit(body_cache_t* bcache, const char* id)
167 168 169
{
  char tmpid[_POSIX_PATH_MAX];

170
  snprintf (tmpid, sizeof (tmpid), "%s.tmp", id);
171

172
  return mutt_bcache_move (bcache, tmpid, id);
173 174
}

175
int mutt_bcache_move(body_cache_t* bcache, const char* id, const char* newid)
176 177 178 179 180 181 182
{
  char path[_POSIX_PATH_MAX];
  char newpath[_POSIX_PATH_MAX];

  if (!bcache || !id || !*id || !newid || !*newid)
    return -1;

183 184
  snprintf (path, sizeof (path), "%s%s", bcache->path, id);
  snprintf (newpath, sizeof (newpath), "%s%s", bcache->path, newid);
185

186
  dprint (3, (debugfile, "bcache: mv: '%s' '%s'\n", path, newpath));
187

188
  return rename (path, newpath);
189 190
}

191
int mutt_bcache_del(body_cache_t *bcache, const char *id)
192 193 194 195 196 197 198
{
  char path[_POSIX_PATH_MAX];

  if (!id || !*id || !bcache)
    return -1;

  path[0] = '\0';
199 200
  safe_strncat (path, sizeof (path), bcache->path, bcache->pathlen);
  safe_strncat (path, sizeof (path), id, mutt_strlen (id));
201

202
  dprint (3, (debugfile, "bcache: del: '%s'\n", path));
203

204
  return unlink (path);
205 206
}

207
int mutt_bcache_exists(body_cache_t *bcache, const char *id)
208 209 210 211 212 213 214 215 216
{
  char path[_POSIX_PATH_MAX];
  struct stat st;
  int rc = 0;

  if (!id || !*id || !bcache)
    return -1;

  path[0] = '\0';
217 218
  safe_strncat (path, sizeof (path), bcache->path, bcache->pathlen);
  safe_strncat (path, sizeof (path), id, mutt_strlen (id));
219

220
  if (stat (path, &st) < 0)
221 222 223 224
    rc = -1;
  else
    rc = S_ISREG(st.st_mode) && st.st_size != 0 ? 0 : -1;

225
  dprint (3, (debugfile, "bcache: exists: '%s': %s\n", path, rc == 0 ? "yes" : "no"));
226 227 228 229

  return rc;
}

230 231 232
int mutt_bcache_list(body_cache_t *bcache,
		     int (*want_id)(const char *id, body_cache_t *bcache,
				    void *data), void *data)
233 234
{
  DIR *d = NULL;
235
  struct dirent *de;
236 237
  int rc = -1;

238
  if (!bcache || !(d = opendir (bcache->path)))
239 240 241 242
    goto out;

  rc = 0;

243
  dprint (3, (debugfile, "bcache: list: dir: '%s'\n", bcache->path));
244

245
  while ((de = readdir (d)))
246
  {
247 248
    if (mutt_strncmp (de->d_name, ".", 1) == 0 ||
	mutt_strncmp (de->d_name, "..", 2) == 0)
249 250
      continue;

251
    dprint (3, (debugfile, "bcache: list: dir: '%s', id :'%s'\n", bcache->path, de->d_name));
252

253
    if (want_id && want_id (de->d_name, bcache, data) != 0)
254 255 256 257 258 259 260 261
      goto out;

    rc++;
  }

out:
  if (d)
  {
262
    if (closedir (d) < 0)
263 264
      rc = -1;
  }
265
  dprint (3, (debugfile, "bcache: list: did %d entries\n", rc));
266 267
  return rc;
}