plugin: implement parser sorting
[platform/upstream/lightmediascanner.git] / src / plugins / png / png.c
1 /**
2  * Copyright (C) 2008-2011 by ProFUSION embedded systems
3  * Copyright (C) 2007 by INdT
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License
7  * as published by the Free Software Foundation; either version 2.1 of
8  * the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful, but
11  * WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18  * 02110-1301 USA
19  *
20  * @author Gustavo Sverzut Barbieri <barbieri@profusion.mobi>
21  */
22
23 /**
24  * @brief
25  *
26  * Reads PNG images.
27  *
28  */
29
30 #include <lightmediascanner_plugin.h>
31 #include <lightmediascanner_utils.h>
32 #include <lightmediascanner_db.h>
33
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <unistd.h>
38 #include <time.h>
39 #include <stdlib.h>
40 #include <stdio.h>
41 #include <string.h>
42 #include <strings.h>
43
44 static const struct lms_string_size dlna_mime =
45     LMS_STATIC_STRING_SIZE("image/png");
46 static const struct lms_string_size dlna_sm_ico =
47     LMS_STATIC_STRING_SIZE("PNG_SM_ICO");
48 static const struct lms_string_size dlna_lrg_ico =
49     LMS_STATIC_STRING_SIZE("PNG_LRG_ICO");
50 static const struct lms_string_size dlna_tn =
51     LMS_STATIC_STRING_SIZE("PNG_TN");
52 static const struct lms_string_size dlna_lrg =
53     LMS_STATIC_STRING_SIZE("PNG_LRG");
54
55 static void
56 _fill_dlna_profile(struct lms_image_info *info)
57 {
58     const unsigned short w = info->width;
59     const unsigned short h = info->height;
60
61     info->dlna_mime = dlna_mime;
62
63     if (w == 0 || h == 0)
64         return;
65
66     if (w == 48 && h == 48)
67         info->dlna_profile = dlna_sm_ico;
68     else if (w == 120 && h == 120)
69         info->dlna_profile = dlna_lrg_ico;
70     else if (w <= 160 && h <= 160)
71         info->dlna_profile = dlna_tn;
72     else if (w <= 4096 && h <= 4096)
73         info->dlna_profile = dlna_lrg;
74 }
75
76 static inline unsigned int
77 _chunk_to_uint(unsigned char *buf)
78 {
79     return (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
80 }
81
82 static int
83 _png_data_get(int fd, struct lms_image_info *info)
84 {
85     unsigned char buf[16], *p;
86     const unsigned char sig[8] = {0x89, 0x50, 0x4e, 0x47, 0xd, 0xa, 0x1a, 0xa};
87     const unsigned char ihdr[4] = {'I', 'H', 'D', 'R'};
88     unsigned int length;
89
90     if (read(fd, buf, sizeof(buf)) != sizeof(buf)) {
91         perror("read");
92         return -1;
93     }
94
95     if (memcmp(buf, sig, sizeof(sig)) != 0) {
96         fprintf(stderr, "ERROR: invalid PNG signature.\n");
97         return -2;
98     }
99
100     p = buf + sizeof(sig) + 4;
101     if (memcmp(p, ihdr, sizeof(ihdr)) != 0) {
102         fprintf(stderr, "ERROR: invalid first chunk: %4.4s.\n", p);
103         return -3;
104     }
105
106     p = buf + sizeof(sig);
107     length = _chunk_to_uint(p);
108     if (length < 13) {
109         fprintf(stderr, "ERROR: IHDR chunk size is too small: %d.\n", length);
110         return -4;
111     }
112
113     if (read(fd, buf, 8) != 8) {
114         perror("read");
115         return -5;
116     }
117     info->width = _chunk_to_uint(buf);
118     info->height = _chunk_to_uint(buf + 4);
119
120     return 0;
121 }
122
123 static const char _name[] = "png";
124 static const struct lms_string_size _exts[] = {
125     LMS_STATIC_STRING_SIZE(".png")
126 };
127 static const char *_cats[] = {
128     "multimedia",
129     "picture",
130     NULL
131 };
132 static const char *_authors[] = {
133     "Gustavo Sverzut Barbieri",
134     NULL
135 };
136
137 struct plugin {
138     struct lms_plugin plugin;
139     lms_db_image_t *img_db;
140 };
141
142 static void *
143 _match(struct plugin *p, const char *path, int len, int base)
144 {
145     long i;
146
147     i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
148     if (i < 0)
149         return NULL;
150     else
151         return (void*)(i + 1);
152 }
153
154 static int
155 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
156 {
157     struct lms_image_info info = { };
158     int fd, r;
159
160     fd = open(finfo->path, O_RDONLY);
161     if (fd < 0) {
162         perror("open");
163         return -1;
164     }
165
166     if (_png_data_get(fd, &info) != 0) {
167         r = -2;
168         goto done;
169     }
170
171     if (info.date == 0)
172         info.date = finfo->mtime;
173
174     if (!info.title.str)
175         lms_name_from_path(&info.title, finfo->path, finfo->path_len,
176                            finfo->base, _exts[((long) match) - 1].len,
177                            NULL);
178     if (info.title.str)
179         lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
180     if (info.artist.str)
181       lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
182
183     _fill_dlna_profile(&info);
184
185     info.id = finfo->id;
186     r = lms_db_image_add(plugin->img_db, &info);
187
188   done:
189     free(info.title.str);
190     free(info.artist.str);
191
192     posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
193     close(fd);
194
195     return r;
196 }
197
198 static int
199 _setup(struct plugin *plugin, struct lms_context *ctxt)
200 {
201     plugin->img_db = lms_db_image_new(ctxt->db);
202     if (!plugin->img_db)
203         return -1;
204
205     return 0;
206 }
207
208 static int
209 _start(struct plugin *plugin, struct lms_context *ctxt)
210 {
211     return lms_db_image_start(plugin->img_db);
212 }
213
214 static int
215 _finish(struct plugin *plugin, struct lms_context *ctxt)
216 {
217     if (plugin->img_db)
218         return lms_db_image_free(plugin->img_db);
219
220     return 0;
221 }
222
223
224 static int
225 _close(struct plugin *plugin)
226 {
227     free(plugin);
228     return 0;
229 }
230
231 API struct lms_plugin *
232 lms_plugin_open(void)
233 {
234     struct plugin *plugin;
235
236     plugin = malloc(sizeof(*plugin));
237     plugin->plugin.name = _name;
238     plugin->plugin.match = (lms_plugin_match_fn_t)_match;
239     plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
240     plugin->plugin.close = (lms_plugin_close_fn_t)_close;
241     plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
242     plugin->plugin.start = (lms_plugin_start_fn_t)_start;
243     plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
244     plugin->plugin.order = 0;
245
246     return (struct lms_plugin *)plugin;
247 }
248
249 API const struct lms_plugin_info *
250 lms_plugin_info(void)
251 {
252     static struct lms_plugin_info info = {
253         _name,
254         _cats,
255         "PNG images",
256         PACKAGE_VERSION,
257         _authors,
258         "http://lms.garage.maemo.org"
259     };
260
261     return &info;
262 }