Add mail-based account recovery
[blerg.git] / common / stringbucket.c
1 /* Blerg is (C) 2011 The Dominion of Awesome, and is distributed under a
2  * BSD-style license.  Please see the COPYING file for details.
3  */
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <sys/mman.h>
7 #include <sys/file.h>
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <stringbucket.h>
14
15 #define STRINGBUCKET_STRINGSIZE 64
16
17 const char * strnchr(const char *s, int c, int n) {
18         const char *ptr = s;
19         while (n > 0) {
20                 if (*ptr == c)
21                         return ptr;
22                 ptr++;
23                 n--;
24         }
25         return NULL;
26 }
27
28 int stringbucket_remap(struct stringbucket *sb) {
29         struct stat st;
30
31         if (sb->list != NULL) {
32                 munmap(sb->list, sb->size);
33         }
34
35         flock(sb->fd, LOCK_SH);
36         fstat(sb->fd, &st);
37         flock(sb->fd, LOCK_UN);
38         sb->size = st.st_size;
39         if (sb->size > 0) {
40                 sb->list = mmap(NULL, sb->size, PROT_READ | PROT_WRITE, MAP_SHARED, sb->fd, 0);
41                 if (sb->list == MAP_FAILED) {
42                         perror("stringbucket mmap");
43                         return 0;
44                 }
45         } else {
46                 /* Don't map anything for now. */
47                 sb->list = NULL;
48         }
49
50         return 1;
51 }
52
53 struct stringbucket * stringbucket_open(const char *filename) {
54         struct stringbucket *obj = malloc(sizeof(struct stringbucket));
55         obj->list = NULL;
56         obj->size = 0;
57
58         if (obj == NULL) {
59                 perror("stringbucket allocate");
60                 goto stringbucket_open__malloc_fail;
61         }
62
63         obj->fd = open(filename, O_RDWR | O_APPEND | O_CREAT, 0600);
64         if (obj->fd == -1) {
65                 perror("stringbucket open");
66                 goto stringbucket_open__open_fail;
67         }
68
69         if (stringbucket_remap(obj) == 0) {
70                 goto stringbucket_open__mmap_fail;
71         }
72
73         return obj;
74
75 stringbucket_open__mmap_fail:
76         close(obj->fd);
77 stringbucket_open__open_fail:
78         free(obj);
79 stringbucket_open__malloc_fail:
80         return NULL;
81 }
82
83 void stringbucket_close(struct stringbucket *sb) {
84         if (sb == NULL)
85                 return;
86
87         if (sb->list != NULL)
88                 munmap(sb->list, sb->size);
89
90         close(sb->fd);
91         free(sb);
92 }
93
94 int stringbucket_find(struct stringbucket *sb, const char *string) {
95         if (sb->list == NULL)
96                 return  -1;
97
98         char * end = sb->list + sb->size;
99         int string_len = strlen(string);
100
101         char * ptr = sb->list;
102         while (ptr < end) {
103                 char * next = (char *) strnchr(ptr, '\n', end - ptr);
104                 if (next == NULL)
105                         next = end;
106                 int len = next - ptr;
107                 if (len > STRINGBUCKET_STRINGSIZE)
108                         len = STRINGBUCKET_STRINGSIZE;
109                 if (memcmp(ptr, string, (len < string_len ? string_len : len)) == 0) {
110                         return (ptr - sb->list);
111                 }
112                 ptr = next + 1;
113         }
114
115         return -1;
116 }
117
118 int stringbucket_add(struct stringbucket *sb, const char *string) {
119         if (stringbucket_find(sb, string) != -1) return 0;
120         int str_len = strlen(string);
121         int len;
122
123         flock(sb->fd, LOCK_EX);
124         len = write(sb->fd, string, str_len);
125         if (len < 0)
126                 perror("stringbucket add");
127         if (len < str_len)
128                 goto stringbucket_add__fail;
129         len = write(sb->fd, "\n", 1);
130         if (len < 0)
131                 perror("stringbucket add");
132         if (len == 0)
133                 goto stringbucket_add__fail;
134         flock(sb->fd, LOCK_UN);
135
136         /* remap the data to include added content */
137         if (stringbucket_remap(sb) == 0)
138                 return 0;
139
140         return 1;
141
142 stringbucket_add__fail:
143         ftruncate(sb->fd, sb->size);
144         flock(sb->fd, LOCK_UN);
145         return 0;
146 }
147
148 int stringbucket_delete(struct stringbucket *sb, const char *string) {
149         int pos = stringbucket_find(sb, string);
150         if (pos == -1) return 0;
151
152         /* We doin' it DOS style! */
153         sb->list[pos] = 0;
154         return 1;
155 }
156
157 void stringbucket_iterate(struct stringbucket *sb, void (*iter)(char *, void *), void *stuff) {
158         if (sb->list == NULL)
159                 return;
160
161         char string[STRINGBUCKET_STRINGSIZE + 1];
162         char * ptr = sb->list;
163         char * end = sb->list + sb->size;
164
165         while (ptr < end) {
166                 char * next = (char *) strnchr(ptr, '\n', end - ptr);
167                 if (next == NULL)
168                         next = end;
169                 if (ptr[0] == 0) {
170                         ptr = next + 1;
171                         continue;
172                 }
173                 int len = next - ptr;
174                 memcpy(string, ptr, len);
175                 string[len] = 0;
176                 iter(string, stuff);
177                 ptr = next + 1;
178         }
179 }