├── javascripts └── main.js ├── images ├── bg_hr.png ├── blacktocat.png ├── icon_download.png └── sprite_download.png ├── README.md ├── stylesheets ├── pygment_trac.css └── stylesheet.css ├── params.json └── index.html /javascripts/main.js: -------------------------------------------------------------------------------- 1 | console.log('This would be the main JS file.'); 2 | -------------------------------------------------------------------------------- /images/bg_hr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lloydsargent/BlackRaccoon/HEAD/images/bg_hr.png -------------------------------------------------------------------------------- /images/blacktocat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lloydsargent/BlackRaccoon/HEAD/images/blacktocat.png -------------------------------------------------------------------------------- /images/icon_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lloydsargent/BlackRaccoon/HEAD/images/icon_download.png -------------------------------------------------------------------------------- /images/sprite_download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lloydsargent/BlackRaccoon/HEAD/images/sprite_download.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BlackRaccoon 2 | IOS FTP Client Code 3 | 4 | THIS FIRMWARE NO LONGER IN ACTIVE DEVELOPMENT NOR IS IT SUPPORTED. 5 | 6 | PLEASE USE EITHER A FORK OR A MORE ROBUST VERSION OF FTP. 7 | 8 | BLACKRACCOON WAS A NON-SECURE METHOD OF FTP USING APPLE'S INTERNAL FUNCTIONS 9 | THAT HAVE SINCE BEEN DEPRECATED/REMOVED. 10 | 11 | Thank you to the many that have used BlackRaccoon and have forked the project. 12 | I hope that it lives on... just with someone else! 13 | 14 | Please note: I have NOT put BlackRaccoon into the Public Domain and STILL retain 15 | all copyright. If you feel the need to use it from another fork, please assure 16 | that the copyright notice remains INTACT. 17 | 18 | Thank you! 19 | -------------------------------------------------------------------------------- /stylesheets/pygment_trac.css: -------------------------------------------------------------------------------- 1 | .highlight .hll { background-color: #ffffcc } 2 | .highlight { background: #f0f3f3; } 3 | .highlight .c { color: #0099FF; font-style: italic } /* Comment */ 4 | .highlight .err { color: #AA0000; background-color: #FFAAAA } /* Error */ 5 | .highlight .k { color: #006699; font-weight: bold } /* Keyword */ 6 | .highlight .o { color: #555555 } /* Operator */ 7 | .highlight .cm { color: #0099FF; font-style: italic } /* Comment.Multiline */ 8 | .highlight .cp { color: #009999 } /* Comment.Preproc */ 9 | .highlight .c1 { color: #0099FF; font-style: italic } /* Comment.Single */ 10 | .highlight .cs { color: #0099FF; font-weight: bold; font-style: italic } /* Comment.Special */ 11 | .highlight .gd { background-color: #FFCCCC; border: 1px solid #CC0000 } /* Generic.Deleted */ 12 | .highlight .ge { font-style: italic } /* Generic.Emph */ 13 | .highlight .gr { color: #FF0000 } /* Generic.Error */ 14 | .highlight .gh { color: #003300; font-weight: bold } /* Generic.Heading */ 15 | .highlight .gi { background-color: #CCFFCC; border: 1px solid #00CC00 } /* Generic.Inserted */ 16 | .highlight .go { color: #AAAAAA } /* Generic.Output */ 17 | .highlight .gp { color: #000099; font-weight: bold } /* Generic.Prompt */ 18 | .highlight .gs { font-weight: bold } /* Generic.Strong */ 19 | .highlight .gu { color: #003300; font-weight: bold } /* Generic.Subheading */ 20 | .highlight .gt { color: #99CC66 } /* Generic.Traceback */ 21 | .highlight .kc { color: #006699; font-weight: bold } /* Keyword.Constant */ 22 | .highlight .kd { color: #006699; font-weight: bold } /* Keyword.Declaration */ 23 | .highlight .kn { color: #006699; font-weight: bold } /* Keyword.Namespace */ 24 | .highlight .kp { color: #006699 } /* Keyword.Pseudo */ 25 | .highlight .kr { color: #006699; font-weight: bold } /* Keyword.Reserved */ 26 | .highlight .kt { color: #007788; font-weight: bold } /* Keyword.Type */ 27 | .highlight .m { color: #FF6600 } /* Literal.Number */ 28 | .highlight .s { color: #CC3300 } /* Literal.String */ 29 | .highlight .na { color: #330099 } /* Name.Attribute */ 30 | .highlight .nb { color: #336666 } /* Name.Builtin */ 31 | .highlight .nc { color: #00AA88; font-weight: bold } /* Name.Class */ 32 | .highlight .no { color: #336600 } /* Name.Constant */ 33 | .highlight .nd { color: #9999FF } /* Name.Decorator */ 34 | .highlight .ni { color: #999999; font-weight: bold } /* Name.Entity */ 35 | .highlight .ne { color: #CC0000; font-weight: bold } /* Name.Exception */ 36 | .highlight .nf { color: #CC00FF } /* Name.Function */ 37 | .highlight .nl { color: #9999FF } /* Name.Label */ 38 | .highlight .nn { color: #00CCFF; font-weight: bold } /* Name.Namespace */ 39 | .highlight .nt { color: #330099; font-weight: bold } /* Name.Tag */ 40 | .highlight .nv { color: #003333 } /* Name.Variable */ 41 | .highlight .ow { color: #000000; font-weight: bold } /* Operator.Word */ 42 | .highlight .w { color: #bbbbbb } /* Text.Whitespace */ 43 | .highlight .mf { color: #FF6600 } /* Literal.Number.Float */ 44 | .highlight .mh { color: #FF6600 } /* Literal.Number.Hex */ 45 | .highlight .mi { color: #FF6600 } /* Literal.Number.Integer */ 46 | .highlight .mo { color: #FF6600 } /* Literal.Number.Oct */ 47 | .highlight .sb { color: #CC3300 } /* Literal.String.Backtick */ 48 | .highlight .sc { color: #CC3300 } /* Literal.String.Char */ 49 | .highlight .sd { color: #CC3300; font-style: italic } /* Literal.String.Doc */ 50 | .highlight .s2 { color: #CC3300 } /* Literal.String.Double */ 51 | .highlight .se { color: #CC3300; font-weight: bold } /* Literal.String.Escape */ 52 | .highlight .sh { color: #CC3300 } /* Literal.String.Heredoc */ 53 | .highlight .si { color: #AA0000 } /* Literal.String.Interpol */ 54 | .highlight .sx { color: #CC3300 } /* Literal.String.Other */ 55 | .highlight .sr { color: #33AAAA } /* Literal.String.Regex */ 56 | .highlight .s1 { color: #CC3300 } /* Literal.String.Single */ 57 | .highlight .ss { color: #FFCC33 } /* Literal.String.Symbol */ 58 | .highlight .bp { color: #336666 } /* Name.Builtin.Pseudo */ 59 | .highlight .vc { color: #003333 } /* Name.Variable.Class */ 60 | .highlight .vg { color: #003333 } /* Name.Variable.Global */ 61 | .highlight .vi { color: #003333 } /* Name.Variable.Instance */ 62 | .highlight .il { color: #FF6600 } /* Literal.Number.Integer.Long */ 63 | 64 | .type-csharp .highlight .k { color: #0000FF } 65 | .type-csharp .highlight .kt { color: #0000FF } 66 | .type-csharp .highlight .nf { color: #000000; font-weight: normal } 67 | .type-csharp .highlight .nc { color: #2B91AF } 68 | .type-csharp .highlight .nn { color: #000000 } 69 | .type-csharp .highlight .s { color: #A31515 } 70 | .type-csharp .highlight .sc { color: #A31515 } 71 | -------------------------------------------------------------------------------- /params.json: -------------------------------------------------------------------------------- 1 | {"note":"Don't delete this file! It's used internally to help with page regeneration.","tagline":"IOS FTP Client Code","name":"Blackraccoon","google":"","body":"## General Notes\r\n\r\nBlackRaccoon is a collection of routines used to act as an FTP client. It was specifically\r\ndesigned to work correctly under ARC and to \"fix\" a leak in the listing of directories.\r\n\r\nWith BlackRaccoon you can:\r\n\r\n*\tDownload a file\r\n*\tUpload a file\r\n*\tDelete a file\r\n*\tCreate a directory\r\n*\tDelete a directory\r\n*\tList a directory\r\n\r\nA function to queue requests has been left in the code, but has not been tested. As this\r\nis not a normal FTP function, the user should assume it is deprecated and will be removed\r\nfrom future releases.\r\n\r\nAs with WhiteRaccoon, the user needs to assure that the **CFNetwork** framework has been\r\nadded to the project.\r\n\r\n### License\r\n\r\nLicense is the MIT License. Basically means you can use as you wish just keep the copyright\r\nnotice in the source code.\r\n\r\n### Differences Between WhiteRaccoon and BlackRaccoon\r\n\r\nBlackRaccoon works correctly with ARC. A lot of time and effort went in to assure there\r\nwere no leaks.\r\n\r\nBlackRaccoon, unlike WhiteRaccoon, breaks up files by objects. This is for this author's\r\nconvenience. If you wish to combine them again, feel free.\r\n\r\nBlackRaccoon has been tested with an unencrypted FTP server. However, it has NOT been\r\ntested with all manner of usernames and passwords.\r\n\r\nAdded helper function *initWithDelegate* to major classes.\r\n\r\nAll FTP operations will either call RequestCompleted for a positive response or\r\nRequestFailed if it is a negative response.\r\n\r\n\r\n### Usage\r\n\r\nThe following code assumes the following:\r\n\r\n\t@interface myclass : NSObject \r\n\t{\r\n\t\tBRRequestCreateDirectory *createDir;\r\n\t\tBRRequestDelete * deleteDir;\r\n\t\tBRRequestListDirectory *listDir;\r\n\t\t\r\n\t\tBRRequestDownload *downloadFile;\r\n\t\tBRRequestUpload *uploadFile;\r\n\t\tBRRequestDelete *deleteFile;\r\n\t}\r\n\r\n\r\n\r\n#### Create Directory\r\n\r\n\t- (IBAction) createDirectory:(id)sender\r\n\t{\r\n \tcreateDir = [BRRequestCreateDirectory initWithDelegate: self];\r\n\t\t\t\t\r\n\t\tcreateDir.path = @\"/home/test/yourdirectory/\";\r\n\t\t\r\n\t\tcreateDir.hostname = @\"192.168.1.5\";\r\n\t\tcreateDir.username = @\"yourusername\";\r\n\t\tcreateDir.password = @\"yourpassword\";\r\n\t\t\r\n\t\t//we start the request\r\n\t\t[createDir start];\r\n\t}\r\n\r\n#### Delete Directory\r\n\r\n\t- (IBAction) deleteDirectory:(id)sender\r\n\t{\r\n\t\tdeleteDir = [BRRequestDelete initWithDelegate: self];\r\n\t\t\t\t\r\n\t\tdeleteDir.path = @\"/home/test/yourdirectory/\";\r\n\t\t\r\n\t\tdeleteDir.hostname = @\"192.168.1.5\";\r\n\t\tdeleteDir.username = @\"yourusername\";\r\n\t\tdeleteDir.password = @\"yourpassword\";\r\n\t\t\r\n\t\t//we start the request\r\n\t\t[deleteDir start];\r\n\t}\r\n\r\n#### List Directory\r\n\r\n\t- (IBAction) listDirectory:(id)sender\r\n\t{\r\n\t\tlistDir = [BRRequestListDirectory initWithDelegate: self];\r\n\t\t\t\r\n\t\tlistDir.path = @\"/home/test/yourdirectory/\";\r\n\t\t\r\n\t\tlistDir.hostname = @\"192.168.1.5\";\r\n\t\tlistDir.username = @\"yourusername\";\r\n\t\tlistDir.password = @\"yourpassword\";\r\n\t\t\r\n\t\t[listDir start];\r\n\t}\r\n\r\n#### Download a File\r\n\r\n\t- (IBAction) downloadFile :(id)sender\r\n\t{\r\n\t\tdownloadFile = [BRRequestDownload initWithDelegate: self];\r\n\t\t\r\n\t\tdownloadFile.path = @\"/home/test/yourfile\";\r\n\t\t\r\n\t\t//for anonymous login just leave the username and password nil\r\n\t\tdownloadFile.hostname = @\"192.168.1.5\";\r\n\t\tdownloadFile.username = @\"yourusername\";\r\n\t\tdownloadFile.password = @\"yourpassword\";\r\n\t\t\r\n\t\t//we start the request\r\n\t\t[downloadFile start];\r\n\t}\r\n\r\n#### Upload a File\r\n\r\n\t- (IBAction) uploadFile :(id)sender\r\n\t{\r\n\t\t//----- get the file to upload as an NSData object\r\n\t\tNSString *applicationDocumentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];\r\n\t\tNSString *filepath = [NSString stringWithFormat: @\"%@/%@\", applicationDocumentsDir, @\"image.jpg\"];\r\n\t\tNSData *dataToUpload = [NSData dataWithContentsOfFile: filepath];\r\n\t\t\r\n\t\tuploadFile = [BRRequestUpload initWithDelegate: self];\r\n\t\t\r\n\t\tuploadFile.sentData = dataToUpload;\r\n\t\t\r\n\t\tuploadFile.path = @\"/home/test/yourfile\";\r\n\t\t\r\n\t\t//for anonymous login just leave the username and password nil\r\n\t\tuploadFile.hostname = @\"192.168.1.5\";\r\n\t\tuploadFile.username = @\"yourusername\";\r\n\t\tuploadFile.password = @\"yourpassword\";\r\n\t\t\r\n\t\t//we start the request\r\n\t\t[uploadFile start];\r\n\t}\r\n\r\n#### Delete a File\r\n\r\n\t- (IBAction) deleteFile: (id) sender\r\n\t{\r\n\t\tdeleteFile = [BRRequestDelete initWithDelegate: self];\r\n\t\t\r\n\t\tdeleteFile.path = @\"/home/test/yourfile\";\r\n\t\t\r\n\t\t//----- for anonymous login just leave the username and password nil\r\n\t\tdeleteFile.hostname = @\"192.168.1.5\";\r\n\t\tdeleteFile.username = @\"yourusername\";\r\n\t\tdeleteFile.password = @\"yourpassword\";\r\n\t\t\r\n\t\t//----- we start the request\r\n\t\t[deleteFile start];\r\n\t}\r\n\r\n#### Delegate callback to determine if something should be overwritten\r\n\r\n\t-(BOOL) shouldOverwriteFileWithRequest: (BRRequest *) request\r\n\t{\r\n\t\t//----- set this as appropriate if you want the file to be overwritten\r\n\t\tif (request == uploadFile)\r\n\t\t{\r\n\t\t\t//----- if uploading a file, we set it to YES\r\n\t\t\treturn YES;\r\n\t\t}\r\n\t\t\r\n\t\t//----- anything else (directories, etc) we set to NO\r\n\t\treturn NO;\r\n\t}\r\n\r\n#### Request Completed\r\n\r\n\t-(void) requestCompleted: (BRRequest *) request\r\n\t{\r\n\t\t//----- handle Create Directory\r\n\t\tif (request == createDir)\r\n\t\t{\r\n\t\t\tNSLog(@\"%@ completed!\", request);\r\n\t\t\t\r\n\t\t\tcreateDir = nil;\r\n\t\t}\r\n\t\t\r\n\t\t//----- handle Delete Directory\r\n\t\tif (request == deleteDir)\r\n\t\t{\r\n\t\t\tNSLog(@\"%@ completed!\", request);\r\n\t\t\t\r\n\t\t\tdeleteDir = nil;\r\n\t\t}\r\n\t\t\r\n\t\t//----- handle List Directory\r\n\t\tif (request == listDir)\r\n\t\t{\r\n\t\t\t//----- called after 'request' is completed successfully\r\n\t\t\tNSLog(@\"%@ completed!\", request);\r\n\t\t\t\r\n\t\t\t//----- we print each of the file names\r\n\t\t\tfor (NSDictionary *file in listDir.filesInfo) \r\n\t\t\t{\r\n\t\t\t\tNSLog(@\"%@\", [file objectForKey: (id) kCFFTPResourceName]);\r\n\t\t\t}\r\n\t\t\t\r\n\t\t\tlogview.text = [NSString stringWithFormat: @\"%@\\n\", logview.text];\r\n\t\t\t[logview scrollRangeToVisible: NSMakeRange([logview.text length] - 1, 1)];\r\n\t\t\t\t\t\t\r\n\t\t\tlistDir = nil;\r\n\t\t}\r\n\t\t\r\n\t\t//----- handle Download File\r\n\t\tif (request == downloadFile)\r\n\t\t{\r\n\t\t\t//called after 'request' is completed successfully\r\n\t\t\tNSLog(@\"%@ completed!\", request);\r\n\t\t\t\r\n\t\t\tNSData *data = downloadFile.receivedData;\r\n\t\t\t\r\n\t\t\t//----- save the NSData as a file object\r\n\t\t\tNSError *error;\r\n\t\t\tNSString *applicationDocumentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];\r\n\t\t\tNSString *filepath = [NSString stringWithFormat: @\"%@/%@\", applicationDocumentsDir, @\"image.jpg\"];\r\n\t\t\t[data writeToFile: filepath options: NSDataWritingFileProtectionNone error: &error];\r\n\t\t\t\r\n\t\t\tdownloadFile = nil;\r\n\t\t}\r\n\t\t\r\n\t\tif (request == uploadFile)\r\n\t\t{\r\n\t\t\tNSLog(@\"%@ completed!\", request);\r\n\t\t\tuploadFile = nil;\r\n\t\t}\r\n\t\t\r\n\t\tif (request == deleteFile)\r\n\t\t{\r\n\t\t\tNSLog(@\"%@ completed!\", request);\r\n\t\t\tdeleteFile = nil;\r\n\t\t}\t\r\n\t}\r\n\r\n#### Request Failed\r\n\r\n\t-(void) requestFailed:(BRRequest *) request\r\n\t{\r\n\t\t//----- handle Create Directory\r\n\t\tif (request == createDir)\r\n\t\t{\r\n\t\t\tNSLog(@\"%@\", request.error.message);\r\n\t\t\t\r\n\t\t\tcreateDir = nil;\r\n\t\t}\r\n\t\t\r\n\t\t//----- handle Delete Directory\r\n\t\tif (request == deleteDir)\r\n\t\t{\r\n\t\t\tNSLog(@\"%@\", request.error.message);\r\n\t\t\t\r\n\t\t\tdeleteDir = nil;\r\n\t\t}\r\n\t\t\r\n\t\t//----- handle List Directory\r\n\t\tif (request == listDir)\r\n\t\t{\r\n\t\t\tNSLog(@\"%@\", request.error.message);\r\n\t\t\t\r\n\t\t\tlistDir = nil;\r\n\t\t}\r\n\t\t\r\n\t\t//----- handle Download a File\r\n\t\tif (request == downloadFile)\r\n\t\t{\r\n\t\t\tNSLog(@\"%@\", request.error.message);\r\n\t\t\t\r\n\t\t\tdownloadFile = nil;\r\n\t\t}\r\n\t\t\r\n\t\t//----- handle Upload a File\r\n\t\tif (request == uploadFile)\r\n\t\t{\r\n\t\t\tNSLog(@\"%@\", request.error.message);\r\n\t\t\t\r\n\t\t\tuploadFile = nil;\r\n\t\t}\r\n\t\t\r\n\t\t//----- handle Delete a File\r\n\t\tif (request == deleteFile)\r\n\t\t{\r\n\t\t\tNSLog(@\"%@\", request.error.message);\r\n\t\t\t\r\n\t\t\tdeleteFile = nil;\r\n\t\t}\r\n\t}\r\n"} -------------------------------------------------------------------------------- /stylesheets/stylesheet.css: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | Slate Theme for Github Pages 3 | by Jason Costello, @jsncostello 4 | *******************************************************************************/ 5 | 6 | @import url(pygment_trac.css); 7 | 8 | /******************************************************************************* 9 | MeyerWeb Reset 10 | *******************************************************************************/ 11 | 12 | html, body, div, span, applet, object, iframe, 13 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 14 | a, abbr, acronym, address, big, cite, code, 15 | del, dfn, em, img, ins, kbd, q, s, samp, 16 | small, strike, strong, sub, sup, tt, var, 17 | b, u, i, center, 18 | dl, dt, dd, ol, ul, li, 19 | fieldset, form, label, legend, 20 | table, caption, tbody, tfoot, thead, tr, th, td, 21 | article, aside, canvas, details, embed, 22 | figure, figcaption, footer, header, hgroup, 23 | menu, nav, output, ruby, section, summary, 24 | time, mark, audio, video { 25 | margin: 0; 26 | padding: 0; 27 | border: 0; 28 | font: inherit; 29 | vertical-align: baseline; 30 | } 31 | 32 | /* HTML5 display-role reset for older browsers */ 33 | article, aside, details, figcaption, figure, 34 | footer, header, hgroup, menu, nav, section { 35 | display: block; 36 | } 37 | 38 | ol, ul { 39 | list-style: none; 40 | } 41 | 42 | blockquote, q { 43 | } 44 | 45 | table { 46 | border-collapse: collapse; 47 | border-spacing: 0; 48 | } 49 | 50 | a:focus { 51 | outline: none; 52 | } 53 | 54 | /******************************************************************************* 55 | Theme Styles 56 | *******************************************************************************/ 57 | 58 | body { 59 | box-sizing: border-box; 60 | color:#373737; 61 | background: #212121; 62 | font-size: 16px; 63 | font-family: 'Myriad Pro', Calibri, Helvetica, Arial, sans-serif; 64 | line-height: 1.5; 65 | -webkit-font-smoothing: antialiased; 66 | } 67 | 68 | h1, h2, h3, h4, h5, h6 { 69 | margin: 10px 0; 70 | font-weight: 700; 71 | color:#222222; 72 | font-family: 'Lucida Grande', 'Calibri', Helvetica, Arial, sans-serif; 73 | letter-spacing: -1px; 74 | } 75 | 76 | h1 { 77 | font-size: 36px; 78 | font-weight: 700; 79 | } 80 | 81 | h2 { 82 | padding-bottom: 10px; 83 | font-size: 32px; 84 | background: url('../images/bg_hr.png') repeat-x bottom; 85 | } 86 | 87 | h3 { 88 | font-size: 24px; 89 | } 90 | 91 | h4 { 92 | font-size: 21px; 93 | } 94 | 95 | h5 { 96 | font-size: 18px; 97 | } 98 | 99 | h6 { 100 | font-size: 16px; 101 | } 102 | 103 | p { 104 | margin: 10px 0 15px 0; 105 | } 106 | 107 | footer p { 108 | color: #f2f2f2; 109 | } 110 | 111 | a { 112 | text-decoration: none; 113 | color: #007edf; 114 | text-shadow: none; 115 | 116 | transition: color 0.5s ease; 117 | transition: text-shadow 0.5s ease; 118 | -webkit-transition: color 0.5s ease; 119 | -webkit-transition: text-shadow 0.5s ease; 120 | -moz-transition: color 0.5s ease; 121 | -moz-transition: text-shadow 0.5s ease; 122 | -o-transition: color 0.5s ease; 123 | -o-transition: text-shadow 0.5s ease; 124 | -ms-transition: color 0.5s ease; 125 | -ms-transition: text-shadow 0.5s ease; 126 | } 127 | 128 | #main_content a:hover { 129 | color: #0069ba; 130 | text-shadow: #0090ff 0px 0px 2px; 131 | } 132 | 133 | footer a:hover { 134 | color: #43adff; 135 | text-shadow: #0090ff 0px 0px 2px; 136 | } 137 | 138 | em { 139 | font-style: italic; 140 | } 141 | 142 | strong { 143 | font-weight: bold; 144 | } 145 | 146 | img { 147 | position: relative; 148 | margin: 0 auto; 149 | max-width: 739px; 150 | padding: 5px; 151 | margin: 10px 0 10px 0; 152 | border: 1px solid #ebebeb; 153 | 154 | box-shadow: 0 0 5px #ebebeb; 155 | -webkit-box-shadow: 0 0 5px #ebebeb; 156 | -moz-box-shadow: 0 0 5px #ebebeb; 157 | -o-box-shadow: 0 0 5px #ebebeb; 158 | -ms-box-shadow: 0 0 5px #ebebeb; 159 | } 160 | 161 | pre, code { 162 | width: 100%; 163 | color: #222; 164 | background-color: #fff; 165 | 166 | font-family: Monaco, "Bitstream Vera Sans Mono", "Lucida Console", Terminal, monospace; 167 | font-size: 14px; 168 | 169 | border-radius: 2px; 170 | -moz-border-radius: 2px; 171 | -webkit-border-radius: 2px; 172 | 173 | 174 | 175 | } 176 | 177 | pre { 178 | width: 100%; 179 | padding: 10px; 180 | box-shadow: 0 0 10px rgba(0,0,0,.1); 181 | overflow: auto; 182 | } 183 | 184 | code { 185 | padding: 3px; 186 | margin: 0 3px; 187 | box-shadow: 0 0 10px rgba(0,0,0,.1); 188 | } 189 | 190 | pre code { 191 | display: block; 192 | box-shadow: none; 193 | } 194 | 195 | blockquote { 196 | color: #666; 197 | margin-bottom: 20px; 198 | padding: 0 0 0 20px; 199 | border-left: 3px solid #bbb; 200 | } 201 | 202 | ul, ol, dl { 203 | margin-bottom: 15px 204 | } 205 | 206 | ul li { 207 | list-style: inside; 208 | padding-left: 20px; 209 | } 210 | 211 | ol li { 212 | list-style: decimal inside; 213 | padding-left: 20px; 214 | } 215 | 216 | dl dt { 217 | font-weight: bold; 218 | } 219 | 220 | dl dd { 221 | padding-left: 20px; 222 | font-style: italic; 223 | } 224 | 225 | dl p { 226 | padding-left: 20px; 227 | font-style: italic; 228 | } 229 | 230 | hr { 231 | height: 1px; 232 | margin-bottom: 5px; 233 | border: none; 234 | background: url('../images/bg_hr.png') repeat-x center; 235 | } 236 | 237 | table { 238 | border: 1px solid #373737; 239 | margin-bottom: 20px; 240 | text-align: left; 241 | } 242 | 243 | th { 244 | font-family: 'Lucida Grande', 'Helvetica Neue', Helvetica, Arial, sans-serif; 245 | padding: 10px; 246 | background: #373737; 247 | color: #fff; 248 | } 249 | 250 | td { 251 | padding: 10px; 252 | border: 1px solid #373737; 253 | } 254 | 255 | form { 256 | background: #f2f2f2; 257 | padding: 20px; 258 | } 259 | 260 | img { 261 | width: 100%; 262 | max-width: 100%; 263 | } 264 | 265 | /******************************************************************************* 266 | Full-Width Styles 267 | *******************************************************************************/ 268 | 269 | .outer { 270 | width: 100%; 271 | } 272 | 273 | .inner { 274 | position: relative; 275 | max-width: 640px; 276 | padding: 20px 10px; 277 | margin: 0 auto; 278 | } 279 | 280 | #forkme_banner { 281 | display: block; 282 | position: absolute; 283 | top:0; 284 | right: 10px; 285 | z-index: 10; 286 | padding: 10px 50px 10px 10px; 287 | color: #fff; 288 | background: url('../images/blacktocat.png') #0090ff no-repeat 95% 50%; 289 | font-weight: 700; 290 | box-shadow: 0 0 10px rgba(0,0,0,.5); 291 | border-bottom-left-radius: 2px; 292 | border-bottom-right-radius: 2px; 293 | } 294 | 295 | #header_wrap { 296 | background: #212121; 297 | background: -moz-linear-gradient(top, #373737, #212121); 298 | background: -webkit-linear-gradient(top, #373737, #212121); 299 | background: -ms-linear-gradient(top, #373737, #212121); 300 | background: -o-linear-gradient(top, #373737, #212121); 301 | background: linear-gradient(top, #373737, #212121); 302 | } 303 | 304 | #header_wrap .inner { 305 | padding: 50px 10px 30px 10px; 306 | } 307 | 308 | #project_title { 309 | margin: 0; 310 | color: #fff; 311 | font-size: 42px; 312 | font-weight: 700; 313 | text-shadow: #111 0px 0px 10px; 314 | } 315 | 316 | #project_tagline { 317 | color: #fff; 318 | font-size: 24px; 319 | font-weight: 300; 320 | background: none; 321 | text-shadow: #111 0px 0px 10px; 322 | } 323 | 324 | #downloads { 325 | position: absolute; 326 | width: 210px; 327 | z-index: 10; 328 | bottom: -40px; 329 | right: 0; 330 | height: 70px; 331 | background: url('../images/icon_download.png') no-repeat 0% 90%; 332 | } 333 | 334 | .zip_download_link { 335 | display: block; 336 | float: right; 337 | width: 90px; 338 | height:70px; 339 | text-indent: -5000px; 340 | overflow: hidden; 341 | background: url(../images/sprite_download.png) no-repeat bottom left; 342 | } 343 | 344 | .tar_download_link { 345 | display: block; 346 | float: right; 347 | width: 90px; 348 | height:70px; 349 | text-indent: -5000px; 350 | overflow: hidden; 351 | background: url(../images/sprite_download.png) no-repeat bottom right; 352 | margin-left: 10px; 353 | } 354 | 355 | .zip_download_link:hover { 356 | background: url(../images/sprite_download.png) no-repeat top left; 357 | } 358 | 359 | .tar_download_link:hover { 360 | background: url(../images/sprite_download.png) no-repeat top right; 361 | } 362 | 363 | #main_content_wrap { 364 | background: #f2f2f2; 365 | border-top: 1px solid #111; 366 | border-bottom: 1px solid #111; 367 | } 368 | 369 | #main_content { 370 | padding-top: 40px; 371 | } 372 | 373 | #footer_wrap { 374 | background: #212121; 375 | } 376 | 377 | 378 | 379 | /******************************************************************************* 380 | Small Device Styles 381 | *******************************************************************************/ 382 | 383 | @media screen and (max-width: 480px) { 384 | body { 385 | font-size:14px; 386 | } 387 | 388 | #downloads { 389 | display: none; 390 | } 391 | 392 | .inner { 393 | min-width: 320px; 394 | max-width: 480px; 395 | } 396 | 397 | #project_title { 398 | font-size: 32px; 399 | } 400 | 401 | h1 { 402 | font-size: 28px; 403 | } 404 | 405 | h2 { 406 | font-size: 24px; 407 | } 408 | 409 | h3 { 410 | font-size: 21px; 411 | } 412 | 413 | h4 { 414 | font-size: 18px; 415 | } 416 | 417 | h5 { 418 | font-size: 14px; 419 | } 420 | 421 | h6 { 422 | font-size: 12px; 423 | } 424 | 425 | code, pre { 426 | min-width: 320px; 427 | max-width: 480px; 428 | font-size: 11px; 429 | } 430 | 431 | } 432 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Blackraccoon 12 | 13 | 14 | 15 | 16 | 17 |
18 |
19 | View on GitHub 20 | 21 |

Blackraccoon

22 |

IOS FTP Client Code

23 | 24 |
25 | Download this project as a .zip file 26 | Download this project as a tar.gz file 27 |
28 |
29 |
30 | 31 | 32 |
33 |
34 |

General Notes

35 | 36 |

BlackRaccoon is a collection of routines used to act as an FTP client. It was specifically 37 | designed to work correctly under ARC and to "fix" a leak in the listing of directories.

38 | 39 |

With BlackRaccoon you can:

40 | 41 |
    42 |
  • Download a file
  • 43 |
  • Upload a file
  • 44 |
  • Delete a file
  • 45 |
  • Create a directory
  • 46 |
  • Delete a directory
  • 47 |
  • List a directory
  • 48 |

A function to queue requests has been left in the code, but has not been tested. As this 49 | is not a normal FTP function, the user should assume it is deprecated and will be removed 50 | from future releases.

51 | 52 |

As with WhiteRaccoon, the user needs to assure that the CFNetwork framework has been 53 | added to the project.

54 | 55 |

License

56 | 57 |

License is the MIT License. Basically means you can use as you wish just keep the copyright 58 | notice in the source code.

59 | 60 |

Differences Between WhiteRaccoon and BlackRaccoon

61 | 62 |

BlackRaccoon works correctly with ARC. A lot of time and effort went in to assure there 63 | were no leaks.

64 | 65 |

BlackRaccoon, unlike WhiteRaccoon, breaks up files by objects. This is for this author's 66 | convenience. If you wish to combine them again, feel free.

67 | 68 |

BlackRaccoon has been tested with an unencrypted FTP server. However, it has NOT been 69 | tested with all manner of usernames and passwords.

70 | 71 |

Added helper function initWithDelegate to major classes.

72 | 73 |

All FTP operations will either call RequestCompleted for a positive response or 74 | RequestFailed if it is a negative response.

75 | 76 |

Usage

77 | 78 |

The following code assumes the following:

79 | 80 |
@interface myclass : NSObject <BRRequestDelegate>
 81 | {
 82 |     BRRequestCreateDirectory *createDir;
 83 |     BRRequestDelete * deleteDir;
 84 |     BRRequestListDirectory *listDir;
 85 | 
 86 |     BRRequestDownload *downloadFile;
 87 |     BRRequestUpload *uploadFile;
 88 |     BRRequestDelete *deleteFile;
 89 | }
 90 | 
91 | 92 |

Create Directory

93 | 94 |
- (IBAction) createDirectory:(id)sender
 95 | {
 96 |     createDir = [BRRequestCreateDirectory initWithDelegate: self];
 97 | 
 98 |     createDir.path = @"/home/test/yourdirectory/";
 99 | 
100 |     createDir.hostname = @"192.168.1.5";
101 |     createDir.username = @"yourusername";
102 |     createDir.password = @"yourpassword";
103 | 
104 |     //we start the request
105 |     [createDir start];
106 | }
107 | 
108 | 109 |

Delete Directory

110 | 111 |
- (IBAction) deleteDirectory:(id)sender
112 | {
113 |     deleteDir = [BRRequestDelete initWithDelegate: self];
114 | 
115 |     deleteDir.path = @"/home/test/yourdirectory/";
116 | 
117 |     deleteDir.hostname = @"192.168.1.5";
118 |     deleteDir.username = @"yourusername";
119 |     deleteDir.password = @"yourpassword";
120 | 
121 |     //we start the request
122 |     [deleteDir start];
123 | }
124 | 
125 | 126 |

List Directory

127 | 128 |
- (IBAction) listDirectory:(id)sender
129 | {
130 |     listDir = [BRRequestListDirectory initWithDelegate: self];
131 | 
132 |     listDir.path = @"/home/test/yourdirectory/";
133 | 
134 |     listDir.hostname = @"192.168.1.5";
135 |     listDir.username = @"yourusername";
136 |     listDir.password = @"yourpassword";
137 | 
138 |     [listDir start];
139 | }
140 | 
141 | 142 |

Download a File

143 | 144 |
- (IBAction) downloadFile :(id)sender
145 | {
146 |     downloadFile = [BRRequestDownload initWithDelegate: self];
147 | 
148 |     downloadFile.path = @"/home/test/yourfile";
149 | 
150 |     //for anonymous login just leave the username and password nil
151 |     downloadFile.hostname = @"192.168.1.5";
152 |     downloadFile.username = @"yourusername";
153 |     downloadFile.password = @"yourpassword";
154 | 
155 |     //we start the request
156 |     [downloadFile start];
157 | }
158 | 
159 | 160 |

Upload a File

161 | 162 |
- (IBAction) uploadFile :(id)sender
163 | {
164 |     //----- get the file to upload as an NSData object
165 |     NSString *applicationDocumentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
166 |     NSString *filepath = [NSString stringWithFormat: @"%@/%@", applicationDocumentsDir, @"image.jpg"];
167 |     NSData *dataToUpload = [NSData dataWithContentsOfFile: filepath];
168 | 
169 |     uploadFile = [BRRequestUpload initWithDelegate: self];
170 | 
171 |     uploadFile.sentData = dataToUpload;
172 | 
173 |     uploadFile.path = @"/home/test/yourfile";
174 | 
175 |     //for anonymous login just leave the username and password nil
176 |     uploadFile.hostname = @"192.168.1.5";
177 |     uploadFile.username = @"yourusername";
178 |     uploadFile.password = @"yourpassword";
179 | 
180 |     //we start the request
181 |     [uploadFile start];
182 | }
183 | 
184 | 185 |

Delete a File

186 | 187 |
- (IBAction) deleteFile: (id) sender
188 | {
189 |     deleteFile = [BRRequestDelete initWithDelegate: self];
190 | 
191 |     deleteFile.path = @"/home/test/yourfile";
192 | 
193 |     //----- for anonymous login just leave the username and password nil
194 |     deleteFile.hostname = @"192.168.1.5";
195 |     deleteFile.username = @"yourusername";
196 |     deleteFile.password = @"yourpassword";
197 | 
198 |     //----- we start the request
199 |     [deleteFile start];
200 | }
201 | 
202 | 203 |

Delegate callback to determine if something should be overwritten

204 | 205 |
-(BOOL) shouldOverwriteFileWithRequest: (BRRequest *) request
206 | {
207 |     //----- set this as appropriate if you want the file to be overwritten
208 |     if (request == uploadFile)
209 |     {
210 |         //----- if uploading a file, we set it to YES
211 |         return YES;
212 |     }
213 | 
214 |     //----- anything else (directories, etc) we set to NO
215 |     return NO;
216 | }
217 | 
218 | 219 |

Request Completed

220 | 221 |
-(void) requestCompleted: (BRRequest *) request
222 | {
223 |     //----- handle Create Directory
224 |     if (request == createDir)
225 |     {
226 |         NSLog(@"%@ completed!", request);
227 | 
228 |         createDir = nil;
229 |     }
230 | 
231 |     //----- handle Delete Directory
232 |     if (request == deleteDir)
233 |     {
234 |         NSLog(@"%@ completed!", request);
235 | 
236 |         deleteDir = nil;
237 |     }
238 | 
239 |     //----- handle List Directory
240 |     if (request == listDir)
241 |     {
242 |         //----- called after 'request' is completed successfully
243 |         NSLog(@"%@ completed!", request);
244 | 
245 |         //----- we print each of the file names
246 |         for (NSDictionary *file in listDir.filesInfo) 
247 |         {
248 |             NSLog(@"%@", [file objectForKey: (id) kCFFTPResourceName]);
249 |         }
250 | 
251 |         logview.text = [NSString stringWithFormat: @"%@\n", logview.text];
252 |         [logview scrollRangeToVisible: NSMakeRange([logview.text length] - 1, 1)];
253 | 
254 |         listDir = nil;
255 |     }
256 | 
257 |     //----- handle Download File
258 |     if (request == downloadFile)
259 |     {
260 |         //called after 'request' is completed successfully
261 |         NSLog(@"%@ completed!", request);
262 | 
263 |         NSData *data = downloadFile.receivedData;
264 | 
265 |         //----- save the NSData as a file object
266 |         NSError *error;
267 |         NSString *applicationDocumentsDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
268 |         NSString *filepath = [NSString stringWithFormat: @"%@/%@", applicationDocumentsDir, @"image.jpg"];
269 |         [data writeToFile: filepath options: NSDataWritingFileProtectionNone error: &error];
270 | 
271 |         downloadFile = nil;
272 |     }
273 | 
274 |     if (request == uploadFile)
275 |     {
276 |         NSLog(@"%@ completed!", request);
277 |         uploadFile = nil;
278 |     }
279 | 
280 |     if (request == deleteFile)
281 |     {
282 |         NSLog(@"%@ completed!", request);
283 |         deleteFile = nil;
284 |     }   
285 | }
286 | 
287 | 288 |

Request Failed

289 | 290 |
-(void) requestFailed:(BRRequest *) request
291 | {
292 |     //----- handle Create Directory
293 |     if (request == createDir)
294 |     {
295 |         NSLog(@"%@", request.error.message);
296 | 
297 |         createDir = nil;
298 |     }
299 | 
300 |     //----- handle Delete Directory
301 |     if (request == deleteDir)
302 |     {
303 |         NSLog(@"%@", request.error.message);
304 | 
305 |         deleteDir = nil;
306 |     }
307 | 
308 |     //----- handle List Directory
309 |     if (request == listDir)
310 |     {
311 |         NSLog(@"%@", request.error.message);
312 | 
313 |         listDir = nil;
314 |     }
315 | 
316 |     //----- handle Download a File
317 |     if (request == downloadFile)
318 |     {
319 |         NSLog(@"%@", request.error.message);
320 | 
321 |         downloadFile = nil;
322 |     }
323 | 
324 |     //----- handle Upload a File
325 |     if (request == uploadFile)
326 |     {
327 |         NSLog(@"%@", request.error.message);
328 | 
329 |         uploadFile = nil;
330 |     }
331 | 
332 |     //----- handle Delete a File
333 |     if (request == deleteFile)
334 |     {
335 |         NSLog(@"%@", request.error.message);
336 | 
337 |         deleteFile = nil;
338 |     }
339 | }
340 | 
341 |
342 |
343 | 344 | 345 | 351 | 352 | 353 | 354 | 355 | 356 | --------------------------------------------------------------------------------