1 module scp; 2 3 import core.stdc.stdlib; 4 import std.stdio; 5 import std.string; 6 import std.file; 7 import std.path; 8 9 import libssh.session; 10 import libssh.scp; 11 import libssh.utils; 12 import libssh.errors; 13 14 import connect_ssh; 15 16 static string[] sources; 17 static string destination; 18 static int verbosity = 0; 19 20 struct location { 21 bool isSSH; 22 string user; 23 string host; 24 string path; 25 SSHSession session; 26 SSHSCP scp; 27 File file; 28 } 29 30 enum Flag { 31 READ, 32 WRITE 33 } 34 35 void usage(string argv0) { 36 stderr.writefln("Usage : %s [options] [[user@]host1:]file1 ... ", argv0); 37 stderr.writeln(" [[user@]host2:]destination"); 38 stderr.writefln("sample scp client - libssh-%s", sshVersion(0)); 39 exit(0); 40 } 41 42 bool opts(string[] argv) { 43 int i = 1; 44 while (i < argv.length) { 45 if (argv[i][0] != '-') { 46 break; 47 } 48 if (argv[i] == "-v") { 49 verbosity++; 50 } else { 51 stderr.writeln("unknown option %s", argv[i][1 .. $]); 52 usage(argv[0]); 53 return false; 54 } 55 i++; 56 } 57 58 auto sourcesCount = cast(int) argv.length - i - 1; 59 if (sourcesCount < 1) { 60 usage(argv[0]); 61 return false; 62 } 63 64 sources = argv[i .. $ - 1]; 65 destination = argv[$ - 1]; 66 67 return true; 68 } 69 70 location parseLocation(string loc) { 71 location result = location.init; 72 73 auto colonIndex = loc.indexOf(':'); 74 if (colonIndex >= 0) { 75 result.isSSH = true; 76 result.path = loc[colonIndex + 1 .. $]; 77 auto atIndex = loc.indexOf('@'); 78 if (atIndex >= 0) { 79 result.host = loc[atIndex + 1 .. colonIndex]; 80 result.user = loc[0 .. atIndex]; 81 } else { 82 result.host = loc[0 .. colonIndex]; 83 } 84 } else { 85 result.isSSH = false; 86 result.path = loc; 87 } 88 return result; 89 } 90 91 bool openLocation(ref location loc, Flag flag) { 92 try { 93 if (loc.isSSH && flag == Flag.WRITE) { 94 loc.session = sessionConnect(loc.host, loc.user, cast(LogVerbosity) verbosity); 95 if (loc.session is null) { 96 return false; 97 } 98 99 loc.scp = loc.session.newScp(SCPMode.Write, loc.path); 100 scope(failure) { 101 loc.scp.dispose(); 102 loc.scp = null; 103 } 104 105 loc.scp.init(); 106 return true; 107 } else if (loc.isSSH && flag == Flag.READ) { 108 loc.session = sessionConnect(loc.host, loc.user, cast(LogVerbosity) verbosity); 109 if (loc.session is null) { 110 return false; 111 } 112 113 loc.scp = loc.session.newScp(SCPMode.Read, loc.path); 114 scope(failure) { 115 loc.scp.dispose(); 116 loc.scp = null; 117 } 118 119 loc.scp.init(); 120 return true; 121 } else { 122 if (isDir(loc.path)) { 123 chdir(loc.path); 124 return true; 125 } 126 127 loc.file = File(loc.path, flag == Flag.READ ? "r" : "w"); 128 return true; 129 } 130 } catch (SSHException sshException) { 131 stderr.writefln("SSH exception. Code = %d, Message:\n%s\n", 132 sshException.errorCode, sshException.msg); 133 return false; 134 } catch (Exception exception) { 135 stderr.writefln("Exception. Message:\n%s\n", exception.msg); 136 return false; 137 } 138 } 139 140 /** @brief copies files from source location to destination 141 * @param src source location 142 * @param dest destination location 143 * @param recursive Copy also directories 144 */ 145 void doCopy(ref location src, ref location dest, bool recursive) { 146 /* recursive mode doesn't work yet */ 147 148 ulong size; 149 uint mode; 150 string fileName; 151 152 if (!src.isSSH) { 153 size = getSize(src.path); 154 mode = getAttributes(src.path) & 0x1ff; 155 fileName = baseName(src.path); 156 } else { 157 size = 0; 158 SCPRequest r; 159 do { 160 r = src.scp.pullRequest(); 161 if (r == SCPRequest.NewDir) { 162 src.scp.denyRequest("Not in recursive mode"); 163 continue; 164 } 165 166 if (r == SCPRequest.NewFile) { 167 size = src.scp.requestSize64(); 168 fileName = src.scp.requestFilename(); 169 mode = src.scp.requestPermissions(); 170 break; 171 } 172 } while (r != SCPRequest.NewFile); 173 } 174 175 if (dest.isSSH) { 176 dest.scp.pushFile64(src.path, size, mode); 177 } else { 178 if (!dest.file.isOpen()) { 179 dest.file = File(fileName, "wb"); 180 } 181 if (src.isSSH) { 182 src.scp.acceptRequest(); 183 } 184 } 185 186 ulong total = 0; 187 ubyte[16384] buffer; 188 ubyte[] outBuffer; 189 190 do { 191 size_t r; 192 if (src.isSSH) { 193 r = src.scp.read(buffer); 194 if (r == 0) { 195 break; 196 } 197 outBuffer = buffer[0 .. r]; 198 } else { 199 outBuffer = src.file.rawRead(buffer); 200 r = outBuffer.length; 201 if (outBuffer.length == 0) { 202 break; 203 } 204 } 205 206 if (dest.isSSH) { 207 dest.scp.write(outBuffer); 208 } else { 209 dest.file.rawWrite(outBuffer); 210 } 211 212 total += r; 213 } while (total < size); 214 215 stdout.writefln("wrote %d bytes", total); 216 } 217 218 int main(string[] argv) { 219 scope(exit) sshFinalize(); 220 221 if (!opts(argv)) { 222 return -1; 223 } 224 225 stdout.writefln("verbose = %d", verbosity); 226 227 try { 228 auto dest = parseLocation(destination); 229 if (!openLocation(dest, Flag.WRITE)) { 230 return -1; 231 } 232 scope(exit) { 233 if (dest.isSSH) { 234 dest.scp.dispose(); 235 dest.session.dispose(); 236 } else { 237 dest.file.close(); 238 } 239 } 240 241 for (int i = 0; i < sources.length; i++) { 242 auto src = parseLocation(sources[i]); 243 if (!openLocation(src, Flag.READ)) { 244 return -1; 245 } 246 scope(exit) { 247 if (src.isSSH) { 248 src.scp.dispose(); 249 src.session.dispose(); 250 } else { 251 src.file.close(); 252 } 253 } 254 255 doCopy(src, dest, false); 256 } 257 258 } catch (SSHException sshException) { 259 stderr.writefln("SSH exception. Code = %d, Message:\n%s\n", 260 sshException.errorCode, sshException.msg); 261 return -1; 262 } 263 264 return 0; 265 }