Advertisement
Guest User

Untitled

a guest
Mar 21st, 2016
91
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.60 KB | None | 0 0
  1. #!/usr/bin/env python2
  2. # -*- coding: utf-8 -*-
  3. """
  4. # FILE: ServerAccess.py
  5. # PURPOSE: Access of Samba File Shares
  6. """
  7.  
  8. import tempfile
  9. import logging
  10. import sys
  11. import os # Path operations
  12. import re
  13.  
  14. from socket import getfqdn
  15.  
  16. from smb.SMBConnection import SMBConnection, OperationFailure
  17. from smb.base import SharedDevice
  18. #from smb.base import SharedFile
  19.  
  20. from .Networking import resolve_network_tuple
  21.  
  22. __DEBUG__ = 9
  23.  
  24. class ServerException(Exception):
  25. pass
  26.  
  27. class ServerAccess(object):
  28. """
  29. Description: ServerAccess Class and File Handler
  30. This is a wrapper and extension class.
  31.  
  32. Uses: PySMB Package by miketeo
  33. URL: github.com/miketeo/pysmb
  34. Mail: miketeo@miketeo.net
  35.  
  36. """
  37. __timeout = 5
  38. __username = None
  39. __password = None
  40. __port = 139
  41. __server_name = None
  42. __server_ip = None
  43. __activeShare = None
  44. __client = None
  45. __logger = logging.getLogger()
  46.  
  47. __server_shares = []
  48.  
  49. # -----------------------------------------------------------------------------
  50. # Default Methods
  51. # -----------------------------------------------------------------------------
  52. def __init__(self, **kwargs):
  53. """
  54. Create an Instance. For further documentation please see the corresponding documentation
  55. on github for the classes of miketeo
  56.  
  57. :uses smb.SMBConnection.__init__
  58. :uses smb.SMBConnection.connect
  59.  
  60. :param **kwargs For details on the needed structure, please refer to the
  61. documentation of the PySMB-Project
  62. """
  63. for key, value in kwargs.items():
  64. self.__setattr__(key, value)
  65. if __DEBUG__ > 3:
  66. if key == "password":
  67. value = 'x'*8
  68. if key != "__logger":
  69. self.__logger.debug(" %s = %s", key, value)
  70.  
  71. self.__client = getfqdn()
  72.  
  73. # use_ntlm_v2 must be passed as named argument
  74. kwargs['use_ntlm_v2'] = False
  75. kwargs['my_name'] = self.__client
  76. kwargs['remote_name'] = self.__server_name
  77. if "shared_folder" in kwargs.keys():
  78. self.shared = kwargs["shared_folder"]
  79. if "timeout" in kwargs.keys():
  80. self.__timeout = kwargs["timeout"]
  81. if "port" in kwargs.keys():
  82. self.__port = kwargs["port"]
  83.  
  84. if not "server_ip" in kwargs.keys():
  85. kwargs["server_ip"] = resolve_network_tuple(kwargs["server_name"])[0]
  86.  
  87. kwargs.pop("server_ip", None)
  88. kwargs.pop("shared_folder", None)
  89. kwargs.pop("timeout", None)
  90. kwargs.pop("server_name", None)
  91.  
  92. self.conn = SMBConnection(**kwargs)
  93.  
  94. self.__logger.debug("Constructor connects to: n%s", self)
  95. assert self.conn.connect("%s"%self.__server_ip, self.__port, timeout=self.__timeout)
  96.  
  97. def __del__(self):
  98. """
  99. Destruction functionality
  100.  
  101. Make sure that connection gets closed
  102.  
  103. # TODO: Check if a job should be finished or not
  104.  
  105. :uses smb.SMBConnection.close
  106. """
  107. try:
  108. self.conn.close()
  109. except AttributeError:
  110. pass
  111.  
  112. def __str__(self):
  113. """
  114. Parse the object to a readable String.
  115.  
  116. returns a xml-structured view ob the object
  117.  
  118. replaces password string by eight times 'x' for IT-Security.
  119. """
  120. result = ("n<Server Access Object>")
  121.  
  122. for key, value in self.__dict__.items():
  123. if key == "__password":
  124. value = 'x'*8
  125. result += "n <{0}>{1}</{0}>".format(key, value)
  126.  
  127. result += ("n</Server Access Object>")
  128.  
  129. return result
  130.  
  131. def __getattr__(self, name):
  132. """
  133. Getter functionality for object attributes.
  134. This getter funtionality is called AFTER other lookup places.
  135.  
  136. :param name Name to be checked for attribute linking.
  137.  
  138. :exception AttributeError No matching getter call was found for the passed parameter
  139. "name" so an AttributeError is raised.
  140.  
  141. :attribute server name Hostname of the target server
  142. :attribute server_ip IP-Address (IPv4, dotted decimal notation) of the target
  143. server
  144. :attribute user Username for authentication requests of target server
  145. :attribute port TCP-Port to be used for the connection to the server
  146. (default-SMB: 139)
  147. :attribute share it is possible that a user has only rights to access one
  148. specific share on a Samba Server.
  149. Therefore we need the possibility to only connect to a
  150. single share as well as to a whole cluster structure
  151. """
  152. name = name.lower()
  153.  
  154. if re.match("server.*name", name, re.I) and not "__" in name:
  155. return self.__server_name
  156. elif re.match("server.*ip", name, re.I) and not "__" in name:
  157. return self.__server_ip
  158. elif name == "user":
  159. return self.__user
  160. elif name == "port":
  161. return self.__port
  162. elif name == "share":
  163. return self.__activeShare
  164. else:
  165. raise AttributeError(name)
  166.  
  167. def __setattr__(self, name, value):
  168. """
  169. Setter functionality for object attributes.
  170.  
  171. This setter function is called before the object dictionary (obj.__dict__) is called.
  172. As we are overloading the setter hereby, we need to update the object dictionary after
  173. implementing our changes.
  174.  
  175. !!! ATTENTION !!!
  176. No setter does automatically call the connect functionality. This is required to be done
  177. afterwards.
  178.  
  179. :attribute *server* Wildcards are used as any "name" containing "server" in any
  180. case combination in it will update the server_name AND the
  181. server_ip of the object by performing a Domain Name Service
  182. call to resolute the Hostname and IP of the passed argument.
  183. Value argument could either be hostname (type<string>) or
  184. IP-Address (IPv4, dotted decimal notation)
  185. :attribute user Username to be set for the connection.
  186. :attribute password Password to be used for the User for Client-Server
  187. authentication(CSA)
  188.  
  189. :uses Networking.resolve_network_tuple
  190.  
  191. """
  192. if ((re.match("server.*name", name.lower(), re.I) or
  193. re.match("server.*ip", name.lower(), re.I)
  194. ) and not "__" in name.lower()):
  195.  
  196. self.__server_ip, self.__server_name = resolve_network_tuple("{}".format(value))
  197. elif name.lower() == "user":
  198. self.__user = value
  199. elif name.lower() == "password":
  200. self.__password = value
  201. else:
  202. self.__dict__[name] = value
  203.  
  204. # -----------------------------------------------------------------------------
  205. # Read-Only Access on Servers
  206. # -----------------------------------------------------------------------------
  207. def list_files(self, share=None, path=""):
  208. """
  209. List all Files in a certain directory or share or combination of both on a connected
  210. remote Samba Server
  211.  
  212. :exceptions ????? For further information on Exceptions that can be raised by
  213. this function, please refer to PySMB Documentation of used
  214. functions.
  215. No Custom Exceptions are raised here.
  216.  
  217. :param share If passed, this will be the share that will be looked into
  218. and __only__ files from this share will be listed.
  219. :param path If passed, only files of this directory/path (if existing
  220. on the selected share) will be listed.
  221.  
  222. :uses smb.SMBConnection.listPath
  223.  
  224. :returns List<String>
  225. """
  226. if share is not None:
  227. return self.conn.listPath(share, path, 55)
  228. elif self.shared:
  229. return self.conn.listPath(self.shared, path, 55)
  230.  
  231. def list_directories(self, share=None, path=""):
  232. """
  233. List all directories in a certain path or share or combination of both on a connected remote
  234. Samba Server
  235.  
  236. :uses self.listFiles
  237.  
  238. :returns List<String>
  239. """
  240. return ([__dir for __dir in self.listFiles(share, path) if __dir.isDirectory])
  241.  
  242. def list_shares(self):
  243. """
  244. List all shares the current logged in user has at least read permissions at the currently
  245. connected remote server.
  246.  
  247. :uses smb.SMBConnection.listShares
  248. :uses self.__timeout
  249.  
  250. :returns self.__ServerShares
  251. """
  252. self.__server_shares = []
  253. if self.conn:
  254. for dev in self.conn.listShares(self.__timeout):
  255. if dev.type == SharedDevice.DISK_TREE:
  256. self.__server_shares.append(dev.name)
  257. return self.__server_shares
  258.  
  259. def get_file(self, filename, share=None):
  260. """
  261. Retrieve a copy of a File object at position 0,0 (=beginning of File) from the connected
  262. remote Server.
  263.  
  264. :param filename relative Filepath to either server or share (if != ""/None)
  265. :param share share from which the file should be retrieved.
  266.  
  267. :uses smb.SMBConnection.retrieveFile
  268. :uses tempfile.NamedTemporaryFile
  269.  
  270. :returns tempfile.NamedTemporaryFile Object
  271. """
  272. file_obj = tempfile.NamedTemporaryFile()
  273. if share is None:
  274. share = self.shared
  275.  
  276. try:
  277. file_attributes, filesize = self.conn.retrieveFile(share, filename, file_obj)
  278. # TODO:
  279. # Use attributes and filesize to check for valid File
  280. # Otherwise all we get is crap
  281. file_obj.seek(0, 0)
  282. self.__logger.debug("Retrieved: %s", filename)
  283. self.__logger.debug("Size: %s", filesize)
  284. self.__logger.debug("Attributes: %s", file_attributes)
  285. except OperationFailure:
  286. file_obj = None
  287. self.__logger.error("Unable to retrieve: %s using %s", filename, self.conn)
  288. return file_obj
  289.  
  290. # -----------------------------------------------------------------------------
  291. # Read-Write Access Functionality
  292. # -----------------------------------------------------------------------------
  293. def write_file(self, file_obj, filename, share=None):
  294. """ write passed file_obj to passed filename on specified share """
  295. if share is None:
  296. self.shared = share
  297. filename = os.path.normpath(filename)
  298. filename = filename.replace('\', '/')
  299.  
  300. __curr_dir = ""
  301. dirnames = os.path.split(filename)
  302.  
  303. for __dir_idx in range(len(dirnames[:-1])):
  304. __curr_dir = "{0}{1}/".format(__curr_dir, dirnames[__dir_idx])
  305. self.writeDirectory("{0}".format(__curr_dir))
  306.  
  307. try:
  308. self.conn.storeFile(self.shared, "{}".format(filename), file_obj)
  309. except:
  310. raise ServerException("Creating File <{0}> failed using {1}".format(filename, self))
  311.  
  312. def write_directory(self, dirpath, share=None):
  313. """ create passed dirpath on specified share """
  314. if share is None:
  315. self.shared = share
  316.  
  317. # Check if Path already exists
  318. if len(self.conn.listPath(self.shared, dirpath)) != 0:
  319. return
  320. try:
  321. self.conn.create_directory(self.shared, dirpath)
  322. except AttributeError:
  323. raise ServerException("Creating Directory <{0}> failed using {1}".format(dirpath, self))
  324.  
  325. def move_file(self, filename1, filename2):
  326. """ move file directly on share """
  327. file_obj = tempfile.NamedTemporaryFile()
  328. file_attributes, filesize = self.conn.retrieveFile(self.shared, filename1, file_obj)
  329. self.conn.storeFile(self.shared, filename2, file_obj)
  330. file_obj.close()
  331.  
  332. self.__logger.debug("Moved: %s to %s", filename1, filename2)
  333. self.__logger.debug("Size: %s", filesize)
  334. self.__logger.debug("Attributes: %s", file_attributes)
  335.  
  336. self.conn.deleteFiles(self.shared, filename1)
  337.  
  338. def delete_file(self, filename, share=None):
  339. """ delete a file on a remote share """
  340. if share is None:
  341. self.shared = share
  342. self.conn.deleteFiles(self.shared, filename)
  343.  
  344. def delete_directory(self, dirpath, share=None):
  345. """ delete a directory on a remote share """
  346. if share is None:
  347. self.shared = share
  348.  
  349. # Check if Path already exists
  350. if len(self.conn.listPath(self.shared, dirpath)) != 0:
  351. return
  352. try:
  353. self.conn.deleteDirectory(self.shared, dirpath)
  354. except:
  355. raise ServerException("Deletion of Directory <{0}>"
  356. " failed using {1}".format(dirpath, self))
  357.  
  358. def sizeof_fmt(num, suffix='B'):
  359. for unit in ['', 'Ki', 'Mi', 'Gi', 'Ti', 'Pi', 'Ei', 'Zi']:
  360. if abs(num) < 1024.0:
  361. return "%3.1f%s%s" % (num, unit, suffix)
  362. num /= 1024.0
  363. return "%.1f%s%s" % (num, 'Yi', suffix)
  364.  
  365. if __name__=="__main__":
  366.  
  367. import argparse
  368. import getpass
  369. from socket import getfqdn
  370.  
  371. from . import Networking
  372.  
  373. LOGGER = logging.getLogger()
  374.  
  375. PARSER = argparse.ArgumentParser(description="Server Access Library")
  376.  
  377. PARSER.add_argument('--host', type=str, required=True, dest='host')
  378. PARSER.add_argument('-u', '--user', type=str, required=True, dest='user')
  379. PARSER.add_argument('-p', '--password', dest='usePW', default=False, action='store_true')
  380. PARSER.add_argument('-s', '--share', type=str, dest='share', default="")
  381.  
  382. ARGS = PARSER.parse_args(sys.argv[1:])
  383.  
  384. HOSTIP, HOSTNAME = Networking.resolve_network_tuple(ARGS.host)
  385.  
  386. LOGGER.debug("%s trying to connect to %s", getfqdn(), HOSTNAME)
  387. if ARGS.usePW:
  388. PASSWORD = getpass.getpass("Please enter password: ")
  389.  
  390. if ARGS.share != "":
  391. APP = ServerAccess(ARGS.user, PASSWORD, getfqdn(), HOSTNAME, HOSTIP, ARGS.share)
  392. print ARGS.share
  393. for shareFile in APP.listFiles(ARGS.share):
  394. if not shareFile.filename in [".", ".."]:
  395. LOGGER.debug(" %s", shareFile.filename)
  396. else:
  397. APP = ServerAccess(ARGS.user, PASSWORD, server_name=HOSTNAME, server_ip=HOSTIP)
  398.  
  399. for _share in APP.listShares():
  400. LOGGER.debug(_share)
  401. for shareFile in APP.listFiles(_share):
  402. if not shareFile.filename in [".",".."]:
  403. LOGGER.debug(" {}".format(shareFile.filename))
  404.  
  405. #!/usr/bin/env python2
  406. # -*- coding=utf-8 -*-
  407. """
  408. # FILE: Networking.py
  409. """
  410.  
  411. import socket
  412. import re
  413. import time
  414. import logging
  415. import sys
  416.  
  417. from errno import ECONNREFUSED
  418.  
  419. __DEBUG__ = 1
  420.  
  421. def resolve_network_tuple(addr, local_logger=None):
  422. """
  423. Resolve addr to receive ip_address Address and Hostname
  424. """
  425.  
  426. ip_address = ""
  427. hostname = ""
  428.  
  429. if local_logger is not None:
  430. local_logger = local_logger
  431. else:
  432. local_logger = logging.getLogger()
  433.  
  434. local_logger.info("Trying to resolve {} ... ".format(addr))
  435. if re.compile(r"^d{1,3}.d{1,3}.d{1,3}.d{1,3}$").match(addr):
  436. # We got an ip_address
  437. local_logger.info("Read address [{}] as ip_address".format(addr))
  438. ip_address = addr
  439. hostname = socket.gethostbyaddr(ip_address)[0]
  440. else:
  441. # We got a Hostname
  442. hostname = addr
  443.  
  444. local_logger.info("Read address [{}] as Hostname".format(hostname))
  445. #we resolve until we get info or max retries reached
  446. __tries = 3
  447. while ip_address == "" and __tries > 0:
  448. local_logger.debug("Tries left {}".format(__tries))
  449. try:
  450. local_logger.info("Resolve {}: {}".format(hostname, socket.getaddrinfo(hostname, 80, socket.AF_INET)))
  451. ip_address = socket.getaddrinfo(hostname, 80, socket.AF_INET)[0][4][0]
  452. except:
  453. ip_address = ""
  454. finally:
  455. time.sleep(0.1)
  456. __tries = __tries - 1
  457.  
  458. local_logger.info("Result: {} | {} (ip_address, Hostname)".format(ip_address, hostname))
  459. return ip_address, hostname
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement