1 /*
2  * Copyright © 2004-2010 Jens Oknelid, paskharen@gmail.com
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  *
18  * In addition, as a special exception, compiling, linking, and/or
19  * using OpenSSL with this program is allowed.
20  */
21 
22 #include "WulforUtil.hh"
23 #include <glib/gi18n.h>
24 #include <glib/gstdio.h>
25 #include <dcpp/ClientManager.h>
26 #include <dcpp/Util.h>
27 #include <dcpp/StringTokenizer.h>
28 #include <iostream>
29 #include <arpa/inet.h>
30 #include <fcntl.h>
31 #include "settingsmanager.hh"
32 #include "dcpp/StringTokenizer.h"
33 #ifdef HAVE_IFADDRS_H
34 #include <ifaddrs.h>
35 #include <net/if.h>
36 #endif
37 #include "extra/magnet.h"
38 
39 using namespace std;
40 using namespace dcpp;
41 
42 const string WulforUtil::ENCODING_LOCALE = _("System default");
43 vector<string> WulforUtil::charsets;
44 const string WulforUtil::magnetSignature = "magnet:?";
45 GtkIconFactory* WulforUtil::iconFactory = NULL;
46 
splitString(const string & str,const string & delimiter)47 vector<int> WulforUtil::splitString(const string &str, const string &delimiter)
48 {
49     string::size_type loc, len, pos = 0;
50     vector<int> array;
51 
52     if (!str.empty() && !delimiter.empty())
53     {
54         while ((loc = str.find(delimiter, pos)) != string::npos)
55         {
56             len = loc - pos;
57             array.push_back(Util::toInt(str.substr(pos, len)));
58             pos = loc + delimiter.size();
59         }
60         len = str.size() - pos;
61         array.push_back(Util::toInt(str.substr(pos, len)));
62     }
63     return array;
64 }
65 
linuxSeparator(const string & ps)66 string WulforUtil::linuxSeparator(const string &ps)
67 {
68     string str = ps;
69     for (auto it = str.begin(); it != str.end(); ++it)
70         if ((*it) == '\\')
71             (*it) = '/';
72     return str;
73 }
74 
windowsSeparator(const string & ps)75 string WulforUtil::windowsSeparator(const string &ps)
76 {
77     string str = ps;
78     for (auto it = str.begin(); it != str.end(); ++it)
79         if ((*it) == '/')
80             (*it) = '\\';
81     return str;
82 }
83 
getLocalIPs()84 vector<string> WulforUtil::getLocalIPs()
85 {
86     vector<string> addresses;
87 
88 #ifdef HAVE_IFADDRS_H
89     struct ifaddrs *ifap;
90 
91     if (getifaddrs(&ifap) == 0)
92     {
93         for (struct ifaddrs *i = ifap; i ; i = i->ifa_next)
94         {
95             struct sockaddr *sa = i->ifa_addr;
96 
97             // If the interface is up, is not a loopback and it has an address
98             if ((i->ifa_flags & IFF_UP) && !(i->ifa_flags & IFF_LOOPBACK) && sa)
99             {
100                 void* src = NULL;
101                 socklen_t len;
102 
103                 // IPv4 address
104                 if (sa->sa_family == AF_INET)
105                 {
106                     struct sockaddr_in* sai = (struct sockaddr_in*)sa;
107                     src = (void*) &(sai->sin_addr);
108                     len = INET_ADDRSTRLEN;
109                 }
110                 // IPv6 address
111                 else if (sa->sa_family == AF_INET6)
112                 {
113                     struct sockaddr_in6* sai6 = (struct sockaddr_in6*)sa;
114                     src = (void*) &(sai6->sin6_addr);
115                     len = INET6_ADDRSTRLEN;
116                 }
117 
118                 // Convert the binary address to a string and add it to the output list
119                 if (src)
120                 {
121                     char address[len];
122                     inet_ntop(sa->sa_family, src, address, len);
123                     addresses.push_back(address);
124                 }
125             }
126         }
127         freeifaddrs(ifap);
128     }
129 #endif
130 
131     return addresses;
132 }
133 
134 
getNicks(const string & cid,const string & hintUrl)135 string WulforUtil::getNicks(const string &cid, const string& hintUrl)
136 {
137     return getNicks(CID(cid), hintUrl);
138 }
139 
getNicks(const CID & cid,const string & hintUrl)140 string WulforUtil::getNicks(const CID& cid, const string& hintUrl)
141 {
142     return Util::toString(ClientManager::getInstance()->getNicks(cid, hintUrl));
143 }
144 
getNicks(const UserPtr & user,const string & hintUrl)145 string WulforUtil::getNicks(const UserPtr& user, const string& hintUrl)
146 {
147     return getNicks(user->getCID(), hintUrl);
148 }
149 
getHubNames(const string & cid,const string & hintUrl)150 string WulforUtil::getHubNames(const string &cid, const string& hintUrl)
151 {
152     return getHubNames(CID(cid), hintUrl);
153 }
154 
getHubNames(const CID & cid,const string & hintUrl)155 string WulforUtil::getHubNames(const CID& cid, const string& hintUrl)
156 {
157     StringList hubs = ClientManager::getInstance()->getHubNames(cid, hintUrl);
158     if (hubs.empty())
159         return _("Offline");
160     else
161         return Util::toString(hubs);
162 }
163 
getHubNames(const UserPtr & user,const string & hintUrl)164 string WulforUtil::getHubNames(const UserPtr& user, const string& hintUrl)
165 {
166     return getHubNames(user->getCID(), hintUrl);
167 }
168 
getHubAddress(const CID & cid,const string & hintUrl)169 StringList WulforUtil::getHubAddress(const CID& cid, const string& hintUrl)
170 {
171     return ClientManager::getInstance()->getHubs(cid, hintUrl);
172 }
173 
getHubAddress(const UserPtr & user,const string & hintUrl)174 StringList WulforUtil::getHubAddress(const UserPtr& user, const string& hintUrl)
175 {
176     return getHubAddress(user->getCID(), hintUrl);
177 }
178 
getTextFromMenu(GtkMenuItem * item)179 string WulforUtil::getTextFromMenu(GtkMenuItem *item)
180 {
181     string text;
182     GtkWidget *child = gtk_bin_get_child(GTK_BIN(item));
183 
184     if (child && GTK_IS_LABEL(child))
185         text = gtk_label_get_text(GTK_LABEL(child));
186 
187     return text;
188 }
189 
getCharsets()190 vector<string>& WulforUtil::getCharsets()
191 {
192     if (charsets.empty())
193     {
194         charsets.push_back(ENCODING_LOCALE);
195         //charsets.push_back(_("UTF-8 (Unicode)"));
196         //charsets.push_back(_("CP1252 (Western Europe)"));
197         //charsets.push_back(_("CP1250 (Central Europe)"));
198         //charsets.push_back(_("ISO-8859-2 (Central Europe)"));
199         //charsets.push_back(_("ISO-8859-7 (Greek)"));
200         //charsets.push_back(_("ISO-8859-8 (Hebrew)"));
201         //charsets.push_back(_("ISO-8859-9 (Turkish)"));
202         //charsets.push_back(_("ISO-2022-JP (Japanese)"));
203         //charsets.push_back(_("SJIS (Japanese)"));
204         //charsets.push_back(_("CP949 (Korean)"));
205         //charsets.push_back(_("KOI8-R (Cyrillic)"));
206         //charsets.push_back(_("CP1251 (Cyrillic)"));
207         //charsets.push_back(_("CP1256 (Arabic)"));
208         //charsets.push_back(_("CP1257 (Baltic)"));
209         //charsets.push_back(_("GB18030 (Chinese)"));
210         //charsets.push_back(_("TIS-620 (Thai)"));
211         charsets.push_back(_("UTF-8"));
212         charsets.push_back(_("CP1252"));
213         charsets.push_back(_("CP1250"));
214         charsets.push_back(_("ISO-8859-2"));
215         charsets.push_back(_("ISO-8859-7"));
216         charsets.push_back(_("ISO-8859-8"));
217         charsets.push_back(_("ISO-8859-9"));
218         charsets.push_back(_("ISO-2022-JP"));
219         charsets.push_back(_("SJIS"));
220         charsets.push_back(_("CP949"));
221         charsets.push_back(_("KOI8-R"));
222         charsets.push_back(_("CP1251"));
223         charsets.push_back(_("CP1256"));
224         charsets.push_back(_("CP1257"));
225         charsets.push_back(_("GB18030"));
226         charsets.push_back(_("TIS-620"));
227     }
228     return charsets;
229 }
230 
openURI(const string & uri)231 void WulforUtil::openURI(const string &uri)
232 {
233     GError* error = NULL;
234     gchar *argv[3];
235     if (!SETTING(MIME_HANDLER).empty())
236         argv[0] = (gchar *)(SETTING(MIME_HANDLER)).c_str();
237     else
238 #if defined(__APPLE__)
239     argv[0] = (gchar *)"open";
240 #elif defined(_WIN32)
241     argv[0] = (gchar *)"start";
242 #else
243     argv[0] = (gchar *)"xdg-open";
244 #endif
245     argv[1] = (gchar *)Text::fromUtf8(uri).c_str();
246     argv[2] = NULL;
247 
248     g_spawn_async(NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &error);
249 
250     if (error)
251     {
252         cerr << "Failed to open URI: " << error->message << endl;
253         g_error_free(error);
254     }
255 }
256 
openURItoApp(const string & cmd)257 void WulforUtil::openURItoApp(const string &cmd)
258 {
259     GError* error = NULL;
260 
261     g_spawn_command_line_async((gchar *)Text::fromUtf8(cmd).c_str(), &error);
262 
263     if (error)
264     {
265         cerr << "Failed to open application: " << error->message << endl;
266         g_error_free(error);
267     }
268 }
269 
colorToString(const GdkColor * color)270 string WulforUtil::colorToString(const GdkColor *color)
271 {
272     gchar strcolor[14];
273 
274     g_snprintf(strcolor, sizeof(strcolor), "#%04X%04X%04X",
275         color->red, color->green, color->blue);
276     //printf("WulforUtil::colorToString{GdkColor} %s\n", strcolor);fflush(stdout);
277     return strcolor;
278 }
279 
280 #if GTK_CHECK_VERSION (3,0,0)
colorToString(const GdkRGBA * color)281 string WulforUtil::colorToString(const GdkRGBA *color)
282 {
283     gchar strcolor[14];
284 
285     g_snprintf(strcolor, sizeof(strcolor), "#%04X%04X%04X",
286         (uint16_t)(color->red*65535.0), (uint16_t)(color->green*65535.0), (uint16_t)(color->blue*65535.0));
287     //printf("WulforUtil::colorToString{GdkRGBA} %s\n",strcolor);fflush(stdout);
288     return strcolor;
289 }
290 #endif
291 
scalePixbuf(const GdkPixbuf * pixbuf,const int width,const int height,GdkInterpType type)292 GdkPixbuf* WulforUtil::scalePixbuf(const GdkPixbuf *pixbuf, const int width, const int height, GdkInterpType type)
293 {
294     g_return_val_if_fail(pixbuf != NULL, NULL);
295     g_return_val_if_fail(width > 0, NULL);
296     g_return_val_if_fail(height > 0, NULL);
297 
298     GdkPixbuf *scale = NULL;
299 
300     int Width = gdk_pixbuf_get_width(pixbuf);
301     int Height = gdk_pixbuf_get_height(pixbuf);
302 
303     double w, h, k;
304 
305     w = (double) width / Width;
306     h = (double) height / Height;
307     k = MIN (w, h);
308 
309     if (Width > width || Height > height)
310 
311         scale = gdk_pixbuf_scale_simple(pixbuf, (int)(Width * k), (int)(Height * k), type);
312     else
313         scale = gdk_pixbuf_scale_simple(pixbuf, (int)(Width * k * 0.85), (int)(Height * k * 0.85), type);
314     return
315         scale;
316 }
317 
makeMagnet(const string & name,const int64_t size,const string & tth)318 string WulforUtil::makeMagnet(const string &name, const int64_t size, const string &tth)
319 {
320     if (name.empty() || tth.empty())
321         return string();
322 
323     // other clients can return paths with different separators, so we should catch both cases
324     string::size_type i = name.find_last_of("/\\");
325     string path = (i != string::npos) ? name.substr(i + 1) : name;
326 
327     return magnetSignature + "xt=urn:tree:tiger:" + tth + "&xl=" + Util::toString(size) + "&dn=" + Util::encodeURI(path);
328 }
329 
splitMagnet(const string & magnet,string & name,int64_t & size,string & tth)330 bool WulforUtil::splitMagnet(const string &magnet, string &name, int64_t &size, string &tth)
331 {
332     name = _("Unknown");
333     size = 0;
334     tth = _("Unknown");
335 
336     StringMap params;
337     if (magnet::parseUri(magnet,params)) {
338         tth=params["xt"];
339         size = Util::toInt64(params["xl"]);
340         name = params["dn"];
341         return TRUE;
342     }
343     return FALSE;
344 }
345 
splitMagnet(const string & magnet,string & line)346 bool WulforUtil::splitMagnet(const string &magnet, string &line)
347 {
348     string name;
349     string tth;
350     int64_t size;
351 
352     if (splitMagnet(magnet, name, size, tth))
353         line = name + " (" + Util::formatBytes(size) + ")";
354     else
355         return FALSE;
356 
357     return TRUE;
358 }
359 
isMagnet(const string & text)360 bool WulforUtil::isMagnet(const string &text)
361 {
362     return g_ascii_strncasecmp(text.c_str(), magnetSignature.c_str(), magnetSignature.length()) == 0;
363 }
364 
isLink(const string & text)365 bool WulforUtil::isLink(const string &text)
366 {
367     return g_ascii_strncasecmp(text.c_str(), "http://", 7) == 0 ||
368         g_ascii_strncasecmp(text.c_str(), "https://", 8) == 0 ||
369         g_ascii_strncasecmp(text.c_str(), "www.", 4) == 0 ||
370         g_ascii_strncasecmp(text.c_str(), "ftp://", 6) == 0 ||
371         g_ascii_strncasecmp(text.c_str(), "sftp://", 7) == 0 ||
372         g_ascii_strncasecmp(text.c_str(), "irc://", 6) == 0 ||
373         g_ascii_strncasecmp(text.c_str(), "ircs://", 7) == 0 ||
374         g_ascii_strncasecmp(text.c_str(), "im:", 3) == 0 ||
375         g_ascii_strncasecmp(text.c_str(), "mailto:", 7) == 0 ||
376         g_ascii_strncasecmp(text.c_str(), "news:", 5) == 0;
377 }
378 
isHubURL(const string & text)379 bool WulforUtil::isHubURL(const string &text)
380 {
381     return g_ascii_strncasecmp(text.c_str(), "dchub://", 8) == 0 ||
382         g_ascii_strncasecmp(text.c_str(), "adc://", 6) == 0 ||
383         g_ascii_strncasecmp(text.c_str(), "adcs://", 7) == 0;
384 }
385 
profileIsLocked()386 bool WulforUtil::profileIsLocked()
387 {
388     static bool profileIsLocked = false;
389 
390     if (profileIsLocked)
391         return TRUE;
392 
393     // We can't use Util::getConfigPath() since the core has not been started yet.
394     // Also, Util::getConfigPath() is utf8 and we need system encoding for g_open().
395     string configPath;
396     char *home = getenv("HOME");
397 #ifdef FORCE_XDG
398     const char *xdg_config_home_ = getenv("XDG_CONFIG_HOME");
399     string xdg_config_home = xdg_config_home_? Text::toUtf8(xdg_config_home_) : (Text::toUtf8(home) +"/.config");
400     xdg_config_home += "/eiskaltdc++/";
401 #else
402     string xdg_config_home = Text::toUtf8(home) + "/.eiskaltdc++/";
403 #endif
404     configPath = !xdg_config_home.empty() ? xdg_config_home : "/tmp/";
405     string profileLockingFile = configPath + "profile.lck";
406     int flags = O_WRONLY | O_CREAT;
407     int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
408 
409     int fd = g_open(profileLockingFile.c_str(), flags, mode);
410     if (fd != -1) // read error
411     {
412         struct flock lock;
413         lock.l_start = 0;
414         lock.l_len = 0;
415         lock.l_type = F_WRLCK;
416         lock.l_whence = SEEK_SET;
417         struct flock testlock = lock;
418 
419         if (fcntl(fd, F_GETLK, &testlock) != -1) // Locking not supported
420         {
421             if (fcntl(fd, F_SETLK, &lock) == -1)
422                 profileIsLocked = true;
423         }
424     }
425 
426     return profileIsLocked;
427 }
428 
429 
getNextIter_gui(GtkTreeModel * model,GtkTreeIter * iter,bool children,bool parent)430 gboolean WulforUtil::getNextIter_gui(GtkTreeModel *model, GtkTreeIter *iter, bool children /* = TRUE */, bool parent /* = TRUE */)
431 {
432     gboolean valid = FALSE;
433     GtkTreeIter old = *iter;
434 
435     if (children && gtk_tree_model_iter_has_child(model, iter))
436     {
437         valid = gtk_tree_model_iter_children(model, iter, &old);
438     }
439     else
440     {
441         valid = gtk_tree_model_iter_next(model, iter);
442     }
443 
444     // Try to go up one level if next failed
445     if (!valid && parent)
446     {
447         valid = gtk_tree_model_iter_parent(model, iter, &old);
448         if (valid)
449             valid = gtk_tree_model_iter_next(model, iter);
450     }
451 
452     return valid;
453 }
454 
copyRow_gui(GtkListStore * store,GtkTreeIter * fromIter,int position)455 GtkTreeIter WulforUtil::copyRow_gui(GtkListStore *store, GtkTreeIter *fromIter, int position /* = -1 */)
456 {
457     GtkTreeIter toIter;
458     gtk_list_store_insert(store, &toIter, position);
459     GtkTreeModel *m = GTK_TREE_MODEL(store);
460     int count = gtk_tree_model_get_n_columns(m);
461 
462     for (int col = 0; col < count; col++)
463     {
464         copyValue_gui(store, fromIter, &toIter, col);
465     }
466 
467     return toIter;
468 }
469 
copyValue_gui(GtkListStore * store,GtkTreeIter * fromIter,GtkTreeIter * toIter,int position)470 void WulforUtil::copyValue_gui(GtkListStore *store, GtkTreeIter *fromIter, GtkTreeIter *toIter, int position)
471 {
472     GValue value = {0, };
473     gtk_tree_model_get_value(GTK_TREE_MODEL(store), fromIter, position, &value);
474     gtk_list_store_set_value(store, toIter, position, &value);
475     g_value_unset(&value);
476 }
477 
copyRow_gui(GtkTreeStore * store,GtkTreeIter * fromIter,GtkTreeIter * parent,int position)478 GtkTreeIter WulforUtil::copyRow_gui(GtkTreeStore *store, GtkTreeIter *fromIter, GtkTreeIter *parent /* = NULL */, int position /* = -1 */)
479 {
480     GtkTreeIter toIter;
481     gtk_tree_store_insert(store, &toIter, parent, position);
482     GtkTreeModel *m = GTK_TREE_MODEL(store);
483     int count = gtk_tree_model_get_n_columns(m);
484 
485     for (int col = 0; col < count; col++)
486     {
487         copyValue_gui(store, fromIter, &toIter, col);
488     }
489 
490     return toIter;
491 }
492 
copyValue_gui(GtkTreeStore * store,GtkTreeIter * fromIter,GtkTreeIter * toIter,int position)493 void WulforUtil::copyValue_gui(GtkTreeStore *store, GtkTreeIter *fromIter, GtkTreeIter *toIter, int position)
494 {
495     GValue value = {0, };
496     gtk_tree_model_get_value(GTK_TREE_MODEL(store), fromIter, position, &value);
497     gtk_tree_store_set_value(store, toIter, position, &value);
498     g_value_unset(&value);
499 }
500 
501 /*
502  * Registers either the custom icons or the GTK+ icons as stock icons in
503  * GtkIconFactory according to the user's preference. If the icons have
504  * previously been loaded, they are removed and re-added.
505  */
registerIcons()506 void WulforUtil::registerIcons()
507 {
508     // Holds a mapping of custom icon names -> stock icon names.
509     // Not all icons have stock representations.
510     WulforSettingsManager *wsm = WulforSettingsManager::getInstance();
511     map<string, string> icons;
512     icons["eiskaltdcpp"] = "eiskaltdcpp";
513     icons["icon_msg"] = wsm->getString("icon_msg");
514     icons["eiskaltdcpp-dc++"] = wsm->getString("icon-dc++");
515     icons["eiskaltdcpp-dc++-fw"] = wsm->getString("icon-dc++-fw");
516     icons["eiskaltdcpp-dc++-fw-op"] = wsm->getString("icon-dc++-fw-op");
517     icons["eiskaltdcpp-dc++-op"] = wsm->getString("icon-dc++-op");
518     icons["eiskaltdcpp-normal"] = wsm->getString("icon-normal");
519     icons["eiskaltdcpp-normal-fw"] = wsm->getString("icon-normal-fw");
520     icons["eiskaltdcpp-normal-fw-op"] = wsm->getString("icon-normal-fw-op");
521     icons["eiskaltdcpp-normal-op"] = wsm->getString("icon-normal-op");
522     icons["icon-smile"] = wsm->getString("icon-smile");
523     icons["icon-download"] = wsm->getString("icon-download");
524     icons["icon-favorite-hubs"] = wsm->getString("icon-favorite-hubs");
525     icons["icon-favorite-users"] = wsm->getString("icon-favorite-users");
526     icons["icon-finished-downloads"] = wsm->getString("icon-finished-downloads");
527     icons["icon-finished-uploads"] = wsm->getString("icon-finished-uploads");
528     icons["icon-hash"] = wsm->getString("icon-hash");
529     icons["icon-preferences"] = wsm->getString("icon-preferences");
530     icons["icon-public-hubs"] = wsm->getString("icon-public-hubs");
531     icons["icon-queue"] = wsm->getString("icon-queue");
532     icons["icon-search"] = wsm->getString("icon-search");
533     icons["icon-search-spy"] = wsm->getString("icon-search-spy");
534     icons["icon-upload"] = wsm->getString("icon-upload");
535     icons["icon-quit"] = wsm->getString("icon-quit");
536     icons["icon-connect"] = wsm->getString("icon-connect");
537     icons["icon-file"] = wsm->getString("icon-file");
538     icons["icon-directory"] = wsm->getString("icon-directory");
539     icons["icon-refresh"] = wsm->getString("icon-refresh");
540     icons["icon-reconnect"] = wsm->getString("icon-reconnect");
541     icons["icon-openlist"] = wsm->getString("icon-openlist");
542     icons["icon-own-filelist"] = wsm->getString("icon-own-filelist");
543     icons["icon-magnet"] = wsm->getString("icon-magnet");
544     icons["icon-search-adl"] = wsm->getString("icon-search-adl");
545     icons["icon-pm-online"] = wsm->getString("icon-pm-online");
546     icons["icon-pm-offline"] = wsm->getString("icon-pm-offline");
547     icons["icon-hub-online"] = wsm->getString("icon-hub-online");
548     icons["icon-hub-offline"] = wsm->getString("icon-hub-offline");
549 
550     if (iconFactory)
551     {
552         gtk_icon_factory_remove_default(iconFactory);
553         iconFactory = NULL;
554     }
555 
556     iconFactory = gtk_icon_factory_new();
557 
558     for (auto i = icons.begin(); i != icons.end(); ++i)
559     {
560         GtkIconSource *iconSource = gtk_icon_source_new();
561         GtkIconSet *iconSet = gtk_icon_set_new();
562         gtk_icon_source_set_icon_name(iconSource, i->second.c_str());
563         gtk_icon_set_add_source(iconSet, iconSource);
564         gtk_icon_factory_add(iconFactory, i->first.c_str(), iconSet);
565         gtk_icon_source_free(iconSource);
566         gtk_icon_set_unref(iconSet);
567     }
568 
569     gtk_icon_factory_add_default(iconFactory);
570     g_object_unref(iconFactory);
571 }
572