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