Merge pull request #4314 from MartijnKaijser/beta1
[vuplus_xbmc] / xbmc / filesystem / AFPFile.cpp
1 /*
2  *      Copyright (C) 2011-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 // FileAFP.cpp: implementation of the CAFPFile class.
22 //
23 //////////////////////////////////////////////////////////////////////
24 #ifdef TARGET_POSIX
25 #include "system.h"
26
27 #if defined(HAS_FILESYSTEM_AFP)
28 #include "AFPFile.h"
29 #include "PasswordManager.h"
30 #include "AFPDirectory.h"
31 #include "Util.h"
32 #include "settings/AdvancedSettings.h"
33 #include "threads/SingleLock.h"
34 #include "utils/log.h"
35 #include "utils/TimeUtils.h"
36
37 using namespace XFILE;
38
39 #define AFP_MAX_READ_SIZE 131072
40
41 void AfpConnectionLog(void *priv, enum loglevels loglevel, int logtype, const char *message)
42 {
43   if (!message) return;
44   CStdString msg = "LIBAFPCLIENT: " + CStdString(message);
45
46   switch(logtype)
47   {
48     case LOG_WARNING:
49       CLog::Log(LOGWARNING, "%s", msg.c_str());
50       break;
51     case LOG_ERR:
52       CLog::Log(LOGERROR, "%s", msg.c_str());
53       break;
54     default:
55       CLog::Log(LOGDEBUG, "%s", msg.c_str());
56       break;
57   }
58 }
59
60 CAfpConnection::CAfpConnection()
61  : m_OpenConnections(0)
62  , m_IdleTimeout(0)
63  , m_pAfpServer(NULL)
64  , m_pAfpVol(NULL)
65  , m_pAfpUrl((struct afp_url*)malloc(sizeof(struct afp_url)))
66  , m_pAfpClient((struct libafpclient*)malloc(sizeof(struct libafpclient)))
67  , m_pLibAfp(new DllLibAfp())
68  , m_bDllInited(false)
69 {
70 }
71
72 CAfpConnection::~CAfpConnection()
73 {
74   Disconnect();
75   free(m_pAfpClient);
76   free(m_pAfpUrl);
77   if (m_pLibAfp->IsLoaded())
78     m_pLibAfp->Unload();
79   delete m_pLibAfp;
80 }
81
82 bool CAfpConnection::initLib()
83 {
84   if (!m_bDllInited)
85   {
86     if (m_pLibAfp->Load())
87     {
88       m_pAfpClient->unmount_volume = NULL;
89       m_pAfpClient->log_for_client = AfpConnectionLog;
90       m_pAfpClient->forced_ending_hook = NULL;
91       m_pAfpClient->scan_extra_fds = NULL;
92       m_pAfpClient->loop_started = NULL;
93
94       m_pLibAfp->libafpclient_register(m_pAfpClient);
95       m_pLibAfp->init_uams();
96       m_pLibAfp->afp_main_quick_startup(NULL);
97       CLog::Log(LOGDEBUG, "AFP: Supported UAMs: %s", m_pLibAfp->get_uam_names_list());
98       m_bDllInited = true;
99     }
100     else
101     {
102       CLog::Log(LOGERROR, "AFP: Error loading afpclient lib.");
103     }
104   }
105
106   return m_bDllInited;
107 }
108
109 //only unmount here - afpclient lib is not
110 //stoppable (no afp_main_quick_shutdown as counter part
111 //for afp_main_quick_startup)
112 void CAfpConnection::Deinit()
113 {
114   if(m_pAfpVol && m_pLibAfp->IsLoaded())
115   {
116     disconnectVolume();
117     Disconnect();
118     m_pAfpUrl->servername[0] = '\0';
119   }        
120 }
121
122 void CAfpConnection::Disconnect()
123 {
124   CSingleLock lock(*this);
125   m_pAfpServer = NULL;
126 }
127
128 void CAfpConnection::disconnectVolume()
129 {
130   if (m_pAfpVol)
131   {
132     // afp_unmount_volume(m_pAfpVol);
133     m_pLibAfp->afp_unmount_all_volumes(m_pAfpServer);
134     m_pAfpVol = NULL;
135   }
136 }
137
138 // taken from cmdline tool
139 bool CAfpConnection::connectVolume(const char *volumename, struct afp_volume *&pVolume)
140 {
141   bool ret = false;
142   if (strlen(volumename) != 0)
143   {
144     // Ah, we're not connected to a volume
145     unsigned int len = 0;
146     char mesg[1024];
147
148     if ((pVolume = m_pLibAfp->find_volume_by_name(m_pAfpServer, volumename)) == NULL)
149     {
150       CLog::Log(LOGDEBUG, "AFP: Could not find a volume called %s\n", volumename);
151     }
152     else
153     {
154       pVolume->mapping = AFP_MAPPING_LOGINIDS;
155       pVolume->extra_flags |= VOLUME_EXTRA_FLAGS_NO_LOCKING;
156
157       if (m_pLibAfp->afp_connect_volume(pVolume, m_pAfpServer, mesg, &len, 1024 ))
158       {
159         CLog::Log(LOGDEBUG, "AFP: Could not access volume %s (error: %s)\n", pVolume->volume_name, mesg);
160         pVolume = NULL;
161       }
162       else
163       {
164         CLog::Log(LOGDEBUG, "AFP: Connected to volume %s\n", pVolume->volume_name_printable);
165         ret = true;
166       }
167     }
168   }
169
170   return ret;
171 }
172
173 CStdString CAfpConnection::getAuthenticatedPath(const CURL &url)
174 {
175   CURL authURL(url);
176   CStdString ret;
177   CPasswordManager::GetInstance().AuthenticateURL(authURL);
178   ret = authURL.Get();
179   return ret;
180 }
181
182 CAfpConnection::afpConnnectError CAfpConnection::Connect(const CURL& url)
183 {
184   CSingleLock lock(*this);
185   struct afp_connection_request *conn_req = NULL;
186   struct afp_url tmpurl;
187   CURL nonConstUrl(getAuthenticatedPath(url)); // we need a editable copy of the url
188   bool serverChanged=false;
189
190   if (!initLib())
191     return AfpFailed;
192
193   m_pLibAfp->afp_default_url(&tmpurl);
194
195   // if hostname has changed - assume server changed
196   if (!nonConstUrl.GetHostName().Equals(m_pAfpUrl->servername, false)|| (m_pAfpServer && m_pAfpServer->connect_state == 0))
197   {
198     serverChanged = true;
199     Disconnect();
200   }
201
202   // if volume changed - also assume server changed (afpclient can't reuse old servobject it seems)
203   if (!nonConstUrl.GetShareName().Equals(m_pAfpUrl->volumename, false))
204   {
205    // no reusing of old server object possible with libafpclient it seems...
206     serverChanged = true;
207     Disconnect();
208   }
209
210   // first, try to parse the URL
211   if (m_pLibAfp->afp_parse_url(&tmpurl, nonConstUrl.Get().c_str(), 0) != 0)
212   {
213     // Okay, this isn't a real URL
214     CLog::Log(LOGDEBUG, "AFP: Could not parse url: %s!\n", nonConstUrl.Get().c_str());
215     return AfpFailed;
216   }
217   else // parsed successfully
218   {
219     // this is our current url object whe are connected to (at least we try)
220     *m_pAfpUrl = tmpurl;
221   }
222
223   // if no username and password is set - use no user authent uam
224   if (strlen(m_pAfpUrl->password) == 0 && strlen(m_pAfpUrl->username) == 0)
225   {
226     // try anonymous
227     strncpy(m_pAfpUrl->uamname, "No User Authent", sizeof(m_pAfpUrl->uamname));
228     CLog::Log(LOGDEBUG, "AFP: Using anonymous authentication.");
229   }
230   else if ((nonConstUrl.GetPassWord().empty() || nonConstUrl.GetUserName().empty()) && serverChanged)
231   {
232     // this is our current url object whe are connected to (at least we try)
233     return AfpAuth;
234   }
235
236   // we got a password in the url
237   if (!nonConstUrl.GetPassWord().empty())
238   {
239     // copy password because afp_parse_url just puts garbage into the password field :(
240     strncpy(m_pAfpUrl->password, nonConstUrl.GetPassWord().c_str(), 127);
241   }
242
243   // whe are not connected or we want to connect to another server
244   if (!m_pAfpServer || serverChanged)
245   {
246     // code from cmdline tool
247     conn_req = (struct afp_connection_request*)malloc(sizeof(struct afp_connection_request));
248     memset(conn_req, 0, sizeof(struct afp_connection_request));
249
250     conn_req->url = *m_pAfpUrl;
251     conn_req->url.requested_version = 31;
252
253     if (strlen(m_pAfpUrl->uamname)>0)
254     {
255       if ((conn_req->uam_mask = m_pLibAfp->find_uam_by_name(m_pAfpUrl->uamname)) == 0)
256       {
257         CLog::Log(LOGDEBUG, "AFP:I don't know about UAM %s\n", m_pAfpUrl->uamname);
258         m_pAfpUrl->volumename[0] = '\0';
259         m_pAfpUrl->servername[0] = '\0';
260         free(conn_req);
261         return AfpFailed;
262       }
263     }
264     else
265     {
266       conn_req->uam_mask = m_pLibAfp->default_uams_mask();
267     }
268
269     // try to connect
270 #ifdef USE_CVS_AFPFS
271     if ((m_pAfpServer = m_pLibAfp->afp_wrap_server_full_connect(NULL, conn_req, NULL)) == NULL)
272 #else
273     if ((m_pAfpServer = m_pLibAfp->afp_wrap_server_full_connect(NULL, conn_req)) == NULL)
274 #endif
275     {
276       m_pAfpUrl->volumename[0] = '\0';
277       m_pAfpUrl->servername[0] = '\0';
278       free(conn_req);
279       CLog::Log(LOGERROR, "AFP: Error connecting to %s", url.Get().c_str());
280       return AfpFailed;
281     }
282     // success!
283     CLog::Log(LOGDEBUG, "AFP: Connected to server %s using UAM \"%s\"\n",
284       m_pAfpServer->server_name, m_pLibAfp->uam_bitmap_to_string(m_pAfpServer->using_uam));
285     // we don't need it after here ...
286     free(conn_req);
287   }
288
289   // if server changed reconnect volume
290   if (serverChanged)
291   {
292     connectVolume(m_pAfpUrl->volumename, m_pAfpVol); // connect new volume
293   }
294   return AfpOk;
295 }
296
297 int CAfpConnection::stat(const CURL &url, struct stat *statbuff)
298 {
299   CSingleLock lock(*this);
300   CStdString strPath = gAfpConnection.GetPath(url);
301   struct afp_volume *pTmpVol = NULL;
302   struct afp_url tmpurl;
303   int iResult = -1;
304   CURL nonConstUrl(getAuthenticatedPath(url)); // we need a editable copy of the url
305
306   if (!initLib() || !m_pAfpServer)
307     return -1;
308
309   m_pLibAfp->afp_default_url(&tmpurl);
310
311   // first, try to parse the URL
312   if (m_pLibAfp->afp_parse_url(&tmpurl, nonConstUrl.Get().c_str(), 0) != 0)
313   {
314     // Okay, this isn't a real URL
315     CLog::Log(LOGDEBUG, "AFP: Could not parse url: %s!\n", nonConstUrl.Get().c_str());
316     return -1;
317   }
318
319   // if no username and password is set - use no user authent uam
320   if (strlen(tmpurl.password) == 0 && strlen(tmpurl.username) == 0)
321   {
322     // try anonymous
323     strncpy(tmpurl.uamname, "No User Authent", sizeof(tmpurl.uamname));
324     CLog::Log(LOGDEBUG, "AFP: Using anonymous authentication.");
325   }
326   else if ((nonConstUrl.GetPassWord().empty() || nonConstUrl.GetUserName().empty()))
327   {
328     // this is our current url object whe are connected to (at least we try)
329     return -1;
330   }
331
332   // we got a password in the url
333   if (!nonConstUrl.GetPassWord().empty())
334   {
335     // copy password because afp_parse_url just puts garbage into the password field :(
336     strncpy(tmpurl.password, nonConstUrl.GetPassWord().c_str(), 127);
337   }
338
339   // connect new volume
340   if(connectVolume(tmpurl.volumename, pTmpVol) && pTmpVol)
341   {
342     iResult = m_pLibAfp->afp_wrap_getattr(pTmpVol, strPath.c_str(), statbuff);
343     //unmount single volume crashs
344     //we will get rid of the mounted volume
345     //once the context is changed in connect function
346     //ppppooooorrrr!!
347     //m_pLibAfp->afp_unmount_volume(pTmpVol);
348   }
349   return iResult;
350 }
351
352
353 /* This is called from CApplication::ProcessSlow() and is used to tell if afp have been idle for too long */
354 void CAfpConnection::CheckIfIdle()
355 {
356   /* We check if there are open connections. This is done without a lock to not halt the mainthread. It should be thread safe as
357    worst case scenario is that m_OpenConnections could read 0 and then changed to 1 if this happens it will enter the if wich will lead to another check, wich is locked.  */
358   if (m_OpenConnections == 0 && m_pAfpVol != NULL)
359   { /* I've set the the maxiumum IDLE time to be 1 min and 30 sec. */
360     CSingleLock lock(*this);
361     if (m_OpenConnections == 0 /* check again - when locked */)
362     {
363       if (m_IdleTimeout > 0)
364       {
365         m_IdleTimeout--;
366       }
367       else
368       {
369         CLog::Log(LOGNOTICE, "AFP is idle. Closing the remaining connections.");
370         gAfpConnection.Deinit();
371       }
372     }
373   }
374 }
375
376 /* The following two function is used to keep track on how many Opened files/directories there are.
377 needed for unloading the dylib*/
378 void CAfpConnection::AddActiveConnection()
379 {
380   CSingleLock lock(*this);
381   m_OpenConnections++;
382 }
383
384 void CAfpConnection::AddIdleConnection()
385 {
386   CSingleLock lock(*this);
387   m_OpenConnections--;
388   /* If we close a file we reset the idle timer so that we don't have any wierd behaviours if a user
389    leaves the movie paused for a long while and then press stop */
390   m_IdleTimeout = 180;
391 }
392
393 CStdString CAfpConnection::GetPath(const CURL &url)
394 {
395   struct afp_url tmpurl;
396   CStdString ret = "";
397
398   m_pLibAfp->afp_default_url(&tmpurl);
399
400   // First, try to parse the URL
401   if (m_pLibAfp->afp_parse_url(&tmpurl, url.Get().c_str(), 0) != 0 )
402   {
403     // Okay, this isn't a real URL
404     CLog::Log(LOGDEBUG, "AFP: Could not parse url.\n");
405   }
406   else
407   {
408     ret = CStdString(tmpurl.path);
409   }
410   return ret;
411 }
412
413 CAfpConnection gAfpConnection;
414
415 CAFPFile::CAFPFile()
416  : m_fileSize(0)
417  , m_fileOffset(0)
418  , m_pFp(NULL)
419  , m_pAfpVol(NULL)
420 {
421   gAfpConnection.AddActiveConnection();
422 }
423
424 CAFPFile::~CAFPFile()
425 {
426   gAfpConnection.AddIdleConnection();
427   Close();
428 }
429
430 int64_t CAFPFile::GetPosition()
431 {
432   if (m_pFp == NULL) return 0;
433   return m_fileOffset;
434 }
435
436 int64_t CAFPFile::GetLength()
437 {
438   if (m_pFp == NULL) return 0;
439   return m_fileSize;
440 }
441
442 bool CAFPFile::Open(const CURL& url)
443 {
444   Close();
445   // we can't open files like afp://file.f or afp://server/file.f
446   // if a file matches the if below return false, it can't exist on a afp share.
447   if (!IsValidFile(url.GetFileName()))
448   {
449     CLog::Log(LOGNOTICE, "FileAfp: Bad URL : '%s'", url.GetFileName().c_str());
450     return false;
451   }
452
453   CSingleLock lock(gAfpConnection);
454   if (gAfpConnection.Connect(url) != CAfpConnection::AfpOk || !gAfpConnection.GetVolume())
455     return false;
456   m_pAfpVol = gAfpConnection.GetVolume();
457
458   CStdString strPath = gAfpConnection.GetPath(url);
459
460   if (gAfpConnection.GetImpl()->afp_wrap_open(m_pAfpVol, strPath.c_str(), O_RDONLY, &m_pFp))
461   {
462     if (gAfpConnection.GetImpl()->afp_wrap_open(m_pAfpVol, CURL::Encode(strPath.c_str()).c_str(), O_RDONLY, &m_pFp))
463     {
464       // write error to logfile
465       CLog::Log(LOGINFO, "CAFPFile::Open: Unable to open file : '%s'\nunix_err:'%x' error : '%s'", strPath.c_str(), errno, strerror(errno));
466       return false;
467     }
468   }
469   
470   CLog::Log(LOGDEBUG,"CAFPFile::Open - opened %s, fd=%d",url.GetFileName().c_str(), m_pFp ? m_pFp->fileid:-1);
471   m_url = url;
472   
473 #ifdef TARGET_POSIX
474   struct __stat64 tmpBuffer;
475 #else
476   struct stat tmpBuffer;
477 #endif  
478   if(Stat(&tmpBuffer))
479   {
480     m_url.Reset();
481     Close();
482     return false;
483   }
484
485   m_fileSize = tmpBuffer.st_size;
486   m_fileOffset = 0;
487   // We've successfully opened the file!
488   return true;
489 }
490
491
492 bool CAFPFile::Exists(const CURL& url)
493 {
494   return Stat(url, NULL) == 0;
495 }
496
497 int CAFPFile::Stat(struct __stat64* buffer)
498 {
499   if (m_pFp == NULL)
500     return -1;
501   return Stat(m_url, buffer);
502 }
503
504 // TODO - maybe check returncode!
505 int CAFPFile::Stat(const CURL& url, struct __stat64* buffer)
506 {
507   CSingleLock lock(gAfpConnection);
508   if (gAfpConnection.Connect(url) != CAfpConnection::AfpOk || !gAfpConnection.GetVolume())
509     return -1;
510
511   CStdString strPath = gAfpConnection.GetPath(url);
512
513   struct stat tmpBuffer = {0};
514   int iResult = gAfpConnection.GetImpl()->afp_wrap_getattr(gAfpConnection.GetVolume(), strPath.c_str(), &tmpBuffer);
515
516   if (buffer)
517   {
518     memset(buffer, 0, sizeof(struct __stat64));
519     buffer->st_dev   = tmpBuffer.st_dev;
520     buffer->st_ino   = tmpBuffer.st_ino;
521     buffer->st_mode  = tmpBuffer.st_mode;
522     buffer->st_nlink = tmpBuffer.st_nlink;
523     buffer->st_uid   = tmpBuffer.st_uid;
524     buffer->st_gid   = tmpBuffer.st_gid;
525     buffer->st_rdev  = tmpBuffer.st_rdev;
526     buffer->st_size  = tmpBuffer.st_size;
527     buffer->st_atime = tmpBuffer.st_atime;
528     buffer->st_mtime = tmpBuffer.st_mtime;
529     buffer->st_ctime = tmpBuffer.st_ctime;
530   }
531
532   return iResult;
533 }
534
535 unsigned int CAFPFile::Read(void *lpBuf, int64_t uiBufSize)
536 {
537   CSingleLock lock(gAfpConnection);
538   if (m_pFp == NULL || !m_pAfpVol)
539     return 0;
540
541   if (uiBufSize > AFP_MAX_READ_SIZE)
542     uiBufSize = AFP_MAX_READ_SIZE;
543
544 #ifdef USE_CVS_AFPFS
545   char *name = m_pFp->basename;
546 #else
547   char *name = m_pFp->name;
548   if (strlen(name) == 0)
549     name = m_pFp->basename;
550
551 #endif
552   int eof = 0;
553   int bytesRead = gAfpConnection.GetImpl()->afp_wrap_read(m_pAfpVol,
554     name, (char *)lpBuf,(size_t)uiBufSize, m_fileOffset, m_pFp, &eof);
555   if (bytesRead > 0)
556     m_fileOffset += bytesRead;
557
558   if (bytesRead < 0)
559   {
560     CLog::Log(LOGERROR, "%s - Error( %d, %d, %s )", __FUNCTION__, bytesRead, errno, strerror(errno));
561     return 0;
562   }
563
564   return (unsigned int)bytesRead;
565 }
566
567 int64_t CAFPFile::Seek(int64_t iFilePosition, int iWhence)
568 {
569   off_t newOffset = m_fileOffset;
570   if (m_pFp == NULL) return -1;
571
572   switch(iWhence)
573   {
574     case SEEK_SET:
575       newOffset = iFilePosition;
576       break;
577     case SEEK_END:
578       newOffset = m_fileSize+iFilePosition;
579       break;
580     case SEEK_CUR:
581       newOffset += iFilePosition;
582       break;
583   }
584
585   if ( newOffset < 0 || newOffset > m_fileSize)
586   {
587     CLog::Log(LOGERROR, "%s - Error( %"PRId64")", __FUNCTION__, newOffset);
588     return -1;
589   }
590
591   m_fileOffset = newOffset;
592   return (int64_t)m_fileOffset;
593 }
594
595 void CAFPFile::Close()
596 {
597   CSingleLock lock(gAfpConnection);
598   if (m_pFp != NULL && m_pAfpVol)
599   {
600     CLog::Log(LOGDEBUG, "CAFPFile::Close closing fd %d", m_pFp->fileid);
601 #ifdef USE_CVS_AFPFS
602     char *name = m_pFp->basename;
603 #else
604     char *name = m_pFp->name;
605     if (strlen(name) == 0)
606       name = m_pFp->basename;
607 #endif
608     gAfpConnection.GetImpl()->afp_wrap_close(m_pAfpVol, name, m_pFp);
609     delete m_pFp;
610     m_pFp = NULL;
611     m_pAfpVol = NULL;
612   }
613 }
614
615 int CAFPFile::Write(const void* lpBuf, int64_t uiBufSize)
616 {
617   CSingleLock lock(gAfpConnection);
618   if (m_pFp == NULL || !m_pAfpVol)
619    return -1;
620
621   int numberOfBytesWritten = 0;
622   uid_t uid;
623   gid_t gid;
624
625   // FIXME need a better way to get server's uid/gid
626   uid = getuid();
627   gid = getgid();
628 #ifdef USE_CVS_AFPFS
629   char *name = m_pFp->basename;
630 #else
631   char *name = m_pFp->name;
632   if (strlen(name) == 0)
633     name = m_pFp->basename;
634 #endif
635   numberOfBytesWritten = gAfpConnection.GetImpl()->afp_wrap_write(m_pAfpVol,
636     name, (const char *)lpBuf, (size_t)uiBufSize, m_fileOffset, m_pFp, uid, gid);
637
638   return numberOfBytesWritten;
639 }
640
641 bool CAFPFile::Delete(const CURL& url)
642 {
643   CSingleLock lock(gAfpConnection);
644   if (gAfpConnection.Connect(url) != CAfpConnection::AfpOk || !gAfpConnection.GetVolume())
645     return false;
646
647   CStdString strPath = gAfpConnection.GetPath(url);
648
649   int result = gAfpConnection.GetImpl()->afp_wrap_unlink(gAfpConnection.GetVolume(), strPath.c_str());
650
651   if (result != 0)
652     CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, strerror(errno));
653
654   return (result == 0);
655 }
656
657 bool CAFPFile::Rename(const CURL& url, const CURL& urlnew)
658 {
659   CSingleLock lock(gAfpConnection);
660   if (gAfpConnection.Connect(url) != CAfpConnection::AfpOk || !gAfpConnection.GetVolume())
661     return false;
662
663   CStdString strFile = gAfpConnection.GetPath(url);
664   CStdString strFileNew = gAfpConnection.GetPath(urlnew);
665
666   int result = gAfpConnection.GetImpl()->afp_wrap_rename(gAfpConnection.GetVolume(), strFile.c_str(), strFileNew.c_str());
667
668   if (result != 0)
669     CLog::Log(LOGERROR, "%s - Error( %s )", __FUNCTION__, strerror(errno));
670
671   return (result == 0);
672 }
673
674 bool CAFPFile::OpenForWrite(const CURL& url, bool bOverWrite)
675 {
676
677   int ret = 0;
678   m_fileSize = 0;
679   m_fileOffset = 0;
680
681   Close();
682   CSingleLock lock(gAfpConnection);
683   if (gAfpConnection.Connect(url) != CAfpConnection::AfpOk || !gAfpConnection.GetVolume())
684     return false;
685
686   // we can't open files like afp://file.f or afp://server/file.f
687   // if a file matches the if below return false, it can't exist on a afp share.
688   if (!IsValidFile(url.GetFileName()))
689     return false;
690
691   m_pAfpVol = gAfpConnection.GetVolume();
692
693   CStdString strPath = gAfpConnection.GetPath(url);
694
695   if (bOverWrite)
696   {
697     CLog::Log(LOGWARNING, "FileAFP::OpenForWrite() called with overwriting enabled! - %s", strPath.c_str());
698     ret = gAfpConnection.GetImpl()->afp_wrap_creat(m_pAfpVol, strPath.c_str(), S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
699   }
700
701   ret = gAfpConnection.GetImpl()->afp_wrap_open(m_pAfpVol, strPath.c_str(), O_RDWR, &m_pFp);
702
703   if (ret || m_pFp == NULL)
704   {
705     // write error to logfile
706     CLog::Log(LOGERROR, "CAFPFile::Open: Unable to open file : '%s'\nunix_err:'%x' error : '%s'", strPath.c_str(), errno, strerror(errno));
707     return false;
708   }
709
710   // We've successfully opened the file!
711   return true;
712 }
713
714 bool CAFPFile::IsValidFile(const CStdString& strFileName)
715 {
716   if (strFileName.find('/') == std::string::npos   || // doesn't have sharename
717       StringUtils::EndsWith(strFileName, "/.") ||     // not current folder
718       StringUtils::EndsWith(strFileName, "/.."))      // not parent folder
719   {
720     return false;
721   }
722
723   return true;
724 }
725 #endif // HAS_FILESYSTEM_AFP
726 #endif // TARGET_POSIX